File Coverage

blib/lib/Template/Sandbox.pm
Criterion Covered Total %
statement 1130 1145 98.6
branch 588 642 91.5
condition 192 225 85.3
subroutine 90 90 100.0
pod 47 47 100.0
total 2047 2149 95.2


line stmt bran cond sub pod time code
1             #
2             # Template::Sandbox: Yet Another Template System.
3             #
4             # Known issues:
5             # - ${VALUE} define replacement breaks char-count and potentially
6             # line count values (if ${VALUE} has newlines).
7             #
8             # Future ideas:
9             #
10              
11             package Template::Sandbox;
12              
13 32     32   796464 use strict;
  32         88  
  32         1380  
14 32     32   189 use warnings;
  32         61  
  32         947  
15              
16 32     32   176 use Carp;
  32         66  
  32         2444  
17 32     32   31077 use Class::Handle;
  32         403739  
  32         521  
18 32     32   31885 use Clone;
  32         119902  
  32         1924  
19 32     32   37200 use Data::Dumper;
  32         357044  
  32         3053  
20 32     32   324 use Digest::MD5;
  32         72  
  32         1081  
21 32     32   31178 use IO::File;
  32         390053  
  32         5050  
22 32     32   329 use File::Spec;
  32         66  
  32         434  
23 32     32   32233 use Log::Any;
  32         91359  
  32         155  
24 32     32   1469 use Scalar::Util;
  32         63  
  32         1671  
25 32     32   37906 use Storable;
  32         138838  
  32         100372  
26             #use Time::HiRes;
27              
28             #my ( @function_table );
29              
30             # Array indices.
31             sub SELF() { 0; }
32             sub OP_LHS() { 1; }
33             sub OP_RHS() { 2; }
34              
35             # Compiled line indices.
36             # TODO: currently unused.
37             sub LINE_INSTR() { 0; }
38             sub LINE_POS() { 1; }
39             sub LINE_ARG() { 2; }
40              
41             # Instruction opcodes.
42             sub LITERAL() { 0; }
43             sub DEBUG() { 1; }
44             sub EXPR() { 2; }
45             sub JUMP() { 3; }
46             sub JUMP_IF() { 4; }
47             sub FOR() { 5; }
48             sub END_FOR() { 6; }
49             sub CONTEXT_PUSH() { 7; }
50             sub CONTEXT_POP() { 8; }
51              
52             # Starting point for opcodes for locally registered syntaxes.
53             sub LOCAL_SYNTAX() { 1_000_000; }
54              
55             # Expression opcodes.
56             sub OP_TREE() { 100; }
57             sub UNARY_OP() { 101; }
58             sub FUNC() { 102; }
59             sub METHOD() { 103; }
60             sub VAR() { 104; }
61             sub TEMPLATE() { 105; }
62              
63             # Template function array indices.
64             sub FUNC_FUNC() { 0; }
65             sub FUNC_ARG_NUM() { 1; }
66             sub FUNC_NEEDS_TEMPLATE() { 2; }
67             sub FUNC_INCONST() { 3; }
68             sub FUNC_UNDEF_OK() { 4; }
69              
70             # Special values in loop vars.
71             sub LOOP_COUNTER() { 0; };
72             sub LOOP_EVEN() { 1; };
73             sub LOOP_ODD() { 2; };
74             sub LOOP_FIRST() { 3; };
75             sub LOOP_INNER() { 4; };
76             sub LOOP_LAST() { 5; };
77             sub LOOP_PREV() { 6; };
78             sub LOOP_NEXT() { 7; };
79             sub LOOP_VALUE() { 8; };
80              
81             my %special_values_names = (
82             __counter__ => LOOP_COUNTER,
83             __even__ => LOOP_EVEN,
84             __odd__ => LOOP_ODD,
85             __first__ => LOOP_FIRST,
86             __inner__ => LOOP_INNER,
87             __last__ => LOOP_LAST,
88             __prev__ => LOOP_PREV,
89             __next__ => LOOP_NEXT,
90             __value__ => LOOP_VALUE,
91             );
92              
93             sub LOOP_STACK_COUNTER() { 0; }
94             sub LOOP_STACK_LAST() { 1; }
95             sub LOOP_STACK_SET() { 2; }
96             sub LOOP_STACK_HASH() { 3; }
97             sub LOOP_STACK_CONTEXT() { 4; }
98             sub LOOP_STACK_SPECIALS() { 5; }
99              
100             # The lower the weight the tighter it binds.
101             my %operators = (
102             # Logic operators
103             'or' => [ 100, sub { $_[ SELF ]->_eval_expression( $_[ OP_LHS ], 1 ) or
104             $_[ SELF ]->_eval_expression( $_[ OP_RHS ], 1 ) },
105             1 ],
106             'and' => [ 99, sub { $_[ SELF ]->_eval_expression( $_[ OP_LHS ], 1 ) and
107             $_[ SELF ]->_eval_expression( $_[ OP_RHS ], 1 ) },
108             1 ],
109             '||' => [ 98, sub { $_[ SELF ]->_eval_expression( $_[ OP_LHS ], 1 ) ||
110             $_[ SELF ]->_eval_expression( $_[ OP_RHS ], 1 ) },
111             1 ],
112             '&&' => [ 96, sub { $_[ SELF ]->_eval_expression( $_[ OP_LHS ], 1 ) &&
113             $_[ SELF ]->_eval_expression( $_[ OP_RHS ], 1 ) },
114             1 ],
115             # Comparison operators
116             'cmp' => [ 95, sub { $_[ OP_LHS ] cmp $_[ OP_RHS ] } ],
117             'ne' => [ 94, sub { $_[ OP_LHS ] ne $_[ OP_RHS ] ? 1 : 0 } ],
118             'eq' => [ 93, sub { $_[ OP_LHS ] eq $_[ OP_RHS ] ? 1 : 0 } ],
119             '<=>' => [ 92, sub { $_[ OP_LHS ] <=> $_[ OP_RHS ] } ],
120             '!=' => [ 91, sub { $_[ OP_LHS ] != $_[ OP_RHS ] ? 1 : 0 } ],
121             '==' => [ 90, sub { $_[ OP_LHS ] == $_[ OP_RHS ] ? 1 : 0 } ],
122             'ge' => [ 89, sub { $_[ OP_LHS ] ge $_[ OP_RHS ] ? 1 : 0 } ],
123             'le' => [ 88, sub { $_[ OP_LHS ] le $_[ OP_RHS ] ? 1 : 0 } ],
124             'gt' => [ 87, sub { $_[ OP_LHS ] gt $_[ OP_RHS ] ? 1 : 0 } ],
125             'lt' => [ 86, sub { $_[ OP_LHS ] lt $_[ OP_RHS ] ? 1 : 0 } ],
126             '>=' => [ 85, sub { $_[ OP_LHS ] >= $_[ OP_RHS ] ? 1 : 0 } ],
127             '<=' => [ 84, sub { $_[ OP_LHS ] <= $_[ OP_RHS ] ? 1 : 0 } ],
128             '>' => [ 83, sub { $_[ OP_LHS ] > $_[ OP_RHS ] ? 1 : 0 } ],
129             '<' => [ 82, sub { $_[ OP_LHS ] < $_[ OP_RHS ] ? 1 : 0 } ],
130              
131             # Assignment
132             '=' => [ 75, sub { $_[ SELF ]->_assign_var( $_[ OP_LHS ],
133             $_[ SELF ]->_eval_expression( $_[ OP_RHS ] ) )
134             },
135             1 ],
136              
137             # Arithmetic/concat
138             '.' => [ 70, sub { $_[ OP_LHS ] . $_[ OP_RHS ] } ],
139             '+' => [ 69, sub { $_[ OP_LHS ] + $_[ OP_RHS ] } ],
140             '-' => [ 68, sub { $_[ OP_LHS ] - $_[ OP_RHS ] } ],
141             '%' => [ 67, sub { $_[ OP_LHS ] % $_[ OP_RHS ] } ],
142             '/' => [ 66, sub { $_[ OP_LHS ] / $_[ OP_RHS ] } ],
143             '*' => [ 65, sub { $_[ OP_LHS ] * $_[ OP_RHS ] } ],
144             );
145              
146             sub def_func
147             {
148 578     578 1 967 my ( $ret, $flag, $val ) = @_;
149 578 100       2730 $ret = [ $ret ] if ref( $ret ) ne 'ARRAY';
150 578         1257 $ret->[ $flag ] = $val;
151             #warn "def_func: ..." . _tersedump( $ret );
152 578         2785 return( $ret );
153             }
154              
155 7     7 1 41 sub inconstant { return( def_func( @_, FUNC_INCONST, 1 ) ); }
156 2     2 1 9 sub needs_template { return( def_func( @_, FUNC_NEEDS_TEMPLATE, 1 ) ); }
157 33     33 1 138 sub undef_ok { return( def_func( @_, FUNC_UNDEF_OK, 1 ) ); }
158 536     536 1 1638 sub has_args { return( def_func( $_[ 0 ], FUNC_ARG_NUM, $_[ 1 ] ) ); }
159 26     26 1 16268 sub no_args { return( has_args( @_, 0 ) ); }
160 457     457 1 12845 sub one_arg { return( has_args( @_, 1 ) ); }
161 14     14 1 30 sub two_args { return( has_args( @_, 2 ) ); }
162 6     6 1 20 sub three_args { return( has_args( @_, 3 ) ); }
163 33     33 1 202 sub any_args { return( has_args( @_, -1 ) ); }
164              
165              
166             # These void() and size() are required since they get used internally
167             # for certain backwards-compat behaviours/syntax sugars.
168             # defined() is required by the test suite, so it stays here too.
169             my %functions = (
170             # Takes any arg and returns '', useful for hiding expression results.
171             void => ( any_args sub { '' } ),
172              
173             size => ( one_arg
174             sub
175             {
176             return( undef ) unless defined( $_[ 0 ] );
177             my $type = Scalar::Util::reftype( $_[ 0 ] );
178             return( $type eq 'HASH' ? scalar( keys( %{$_[ 0 ]} ) ) :
179             $type eq 'ARRAY' ? scalar( @{$_[ 0 ]} ) :
180             $type eq 'SCALAR' ? length( ${$_[ 0 ]} ) :
181             length( $_[ 0 ] ) );
182             } ),
183              
184             defined => ( one_arg undef_ok sub { defined( $_[ 0 ] ) ? 1 : 0 } ),
185             );
186              
187             #print "Content-type: text/plain\n\n" . Data::Dumper::Dumper( \%functions );
188              
189             my %token_aliases = (
190             'foreach' => 'for',
191             'end for' => 'endfor',
192             'endforeach' => 'endfor',
193             'end foreach' => 'endfor',
194             'end include' => 'endinclude',
195             'els if' => 'elsif',
196             'else if' => 'elsif',
197             'elseif' => 'elsif',
198             'end if' => 'endif',
199             'els unless' => 'elsunless',
200             'else unless' => 'elsunless',
201             'elseunless' => 'elsunless',
202             'end unless' => 'endunless',
203             );
204              
205             # zero_width => boolean,
206             # Zero-width tokens gobble one of the surrounding \n if they're
207             # on a line by themselves, preventing "blank-line spam" in the
208             # template output.
209             # TODO: move syntaxes into substructure to avoid .key hackery.
210             my %syntaxes = (
211             # Faux values to define the auto opcode generation for local syntaxes.
212             '.next_instr' => LOCAL_SYNTAX,
213             '.instr_increment' => 1,
214             '.instr' => {},
215              
216             # Actual syntax definitions.
217             'var' => {
218             positional_args => [ 'var' ],
219             valid_args => [ 'var' ],
220             },
221             'debug' => {
222             positional_args => [ 'type', 'state' ],
223             valid_args => [ 'type', 'state' ],
224             zero_width => 1,
225             },
226             '#' => {
227             zero_width => 1,
228             },
229             'include' => {
230             positional_args => [ 'filename' ],
231             zero_width => 1,
232             },
233             'endinclude' => {
234             zero_width => 1,
235             },
236             'for' => {
237             zero_width => 1,
238             },
239             'endfor' => {
240             zero_width => 1,
241             },
242             'if' => {
243             zero_width => 1,
244             },
245             'unless' => {
246             zero_width => 1,
247             },
248             'else' => {
249             zero_width => 1,
250             },
251             'elsif' => {
252             zero_width => 1,
253             },
254             'elsunless' => {
255             zero_width => 1,
256             },
257             'endif' => {
258             zero_width => 1,
259             },
260             'endunless' => {
261             zero_width => 1,
262             },
263             );
264              
265             # Special vars that are symbolic literals.
266             my %symbolic_literals = (
267             'undef' => [ LITERAL, 'undef', undef ],
268             'null' => [ LITERAL, 'null', undef ],
269             'cr' => [ LITERAL, 'cr', "\n" ],
270             );
271              
272             # "our" declarations are to work around problem in some perls where
273             # "my" scope variables aren't seen by (??{ ... }).
274             our ( $single_quoted_text_regexp );
275              
276             $single_quoted_text_regexp = qr/
277             \'
278             (?:
279             # Quoteless, backslashless text.
280             (?> [^\'\\]+ )
281             |
282             # Escaped characters.
283             (?> (?:\\\\)* \\ . )
284             )*
285             \'
286             /sxo;
287              
288             our ( $double_quoted_text_regexp );
289              
290             $double_quoted_text_regexp = qr/
291             \"
292             (?:
293             # Quoteless, backslashless text.
294             (?> [^\"\\]+ )
295             |
296             # Escaped characters.
297             (?> (?:\\\\)* \\ . )
298             )*
299             \"
300             /sxo;
301              
302             our ( $matching_square_brackets_regexp );
303              
304             $matching_square_brackets_regexp = qr/
305             \[
306             (?:
307             # Bracketless, quoteless subtext.
308             (?> [^\[\]\"\']+ )
309             |
310             # Quoted text.
311             (??{ $double_quoted_text_regexp }) |
312             (??{ $single_quoted_text_regexp })
313             |
314             # Expression containing sub-brackets.
315             (??{ $matching_square_brackets_regexp })
316             )*
317             \]
318             /sxo;
319              
320             our ( $matching_round_brackets_regexp );
321              
322             $matching_round_brackets_regexp = qr/
323             \(
324             (?:
325             # Bracketless, quoteless subtext.
326             (?> [^\(\)\"\']+ )
327             |
328             # Quoted text.
329             (??{ $double_quoted_text_regexp }) |
330             (??{ $single_quoted_text_regexp })
331             |
332             # Expression containing sub-brackets.
333             (??{ $matching_round_brackets_regexp })
334             )*
335             \)
336             /sxo;
337              
338             my $bare_identifier_regexp = qr/
339             [a-zA-Z_][a-zA-Z0-9_]*
340             /sxo;
341              
342             my $function_regexp = qr/
343             # abc( expr )
344             $bare_identifier_regexp
345             $matching_round_brackets_regexp
346             /sxo;
347              
348             my $capture_function_regexp = qr/
349             ^
350             # abc( expr )
351             ($bare_identifier_regexp)
352             \(
353             \s*
354             (.*?)
355             \s*
356             \)
357             $
358             /sxo;
359              
360             # Chained structure:
361             # var.sub.sub
362             # var.sub['sub']
363             # var['sub'].sub
364             # var['sub']['sub'] fails!
365             # var.sub.method()
366             # var['sub'].method()
367             # var.method().sub
368             # var.method()['sub']
369             # func().sub.sub ?
370             # func().method().sub
371              
372             my $subscript_operator_regexp = qr/
373             (?: \. | \-\> )
374             /sxo;
375              
376             my $expr_subscript_regexp = $matching_square_brackets_regexp;
377             my $capture_expr_subscript_regexp = qr/
378             ^
379             \[
380             \s*
381             (.*?)
382             \s*
383             \]
384             $
385             /sxo;
386             my $literal_subscript_regexp = qr/
387             $subscript_operator_regexp
388             $bare_identifier_regexp
389             /sxo;
390             my $capture_literal_subscript_regexp = qr/
391             ^
392             $subscript_operator_regexp
393             ($bare_identifier_regexp)
394             $
395             /sxo;
396             my $method_subscript_regexp = qr/
397             $subscript_operator_regexp
398             $function_regexp
399             /sxo;
400             my $capture_method_subscript_regexp = qr/
401             ^
402             # . | ->
403             $subscript_operator_regexp
404             # abc( expr )
405             ($bare_identifier_regexp)
406             \(
407             \s*
408             (.*?)
409             \s*
410             \)
411             # ($matching_round_brackets_regexp)
412             $
413             /sxo;
414              
415             my $chained_operation_top_regexp = qr/
416             (?:
417             # Function goes first to take matching precedence over bareword
418             $function_regexp |
419             $bare_identifier_regexp
420             )
421             /sxo;
422             my $chained_operation_subscript_regexp = qr/
423             (?:
424             $expr_subscript_regexp |
425             # Method goes first to take matching precedence over bareword
426             $method_subscript_regexp |
427             $literal_subscript_regexp
428             )
429             /sxo;
430             my $chained_operation_regexp = qr/
431             $chained_operation_top_regexp
432             (?: $chained_operation_subscript_regexp )*
433             /sxo;
434              
435             my $capture_chained_operation_top_regexp = qr/
436             ^
437             ($chained_operation_top_regexp)
438             # we don't care at this point what the rest of the crud is.
439             (.*)
440             $
441             /sxo;
442             my $capture_chained_operation_subscript_regexp = qr/
443             ^
444             ($chained_operation_subscript_regexp)
445             # we don't care at this point what the rest of the crud is.
446             (.*)
447             $
448             /sxo;
449              
450             my $literal_number_regexp = qr/
451             # 1 or more digits.
452             \d+
453             # Optionally a decimal fraction.
454             (?: \. \d+ )?
455             /sxo;
456              
457             my $unary_operator_regexp = qr/
458             (?: \! | not (?=\s) | - )
459             /sxo;
460             my $capture_unary_operator_regexp = qr/
461             ^
462             ( \! | not (?=\s) | - )
463             \s*
464             (.*)
465             $
466             /sxo;
467              
468             my $atomic_expr_regexp = qr/
469             # Optionally a unary operator
470             (?: $unary_operator_regexp \s* )?
471             # Followed by an atomic value
472             (?:
473             # A bracketed sub-expression.
474             $matching_round_brackets_regexp
475             |
476             # A chained operation.
477             $chained_operation_regexp
478             |
479             # A literal number
480             $literal_number_regexp
481             |
482             # A literal string
483             $single_quoted_text_regexp
484             )
485             /sxo;
486              
487             my $operator_regexp = join( '|', map { "\Q$_\E" } keys( %operators ) );
488             $operator_regexp = qr/
489             (?: $operator_regexp )
490             /sxo;
491              
492             my $expr_regexp = qr/
493             \s*
494             (?:
495             # A sequence of atomic epressions and operators.
496             $atomic_expr_regexp
497             (?:
498             \s+
499             $operator_regexp
500             \s+
501             $atomic_expr_regexp
502             )*
503             )
504             \s*
505             /sxo;
506             # (?:
507             # \s+
508             # $operator_regexp
509             # \s+
510             # $atomic_expr_regexp
511             # )
512             # |
513             # (?:
514             # \s+
515             # \?
516             # \s+
517             # $atomic_expr_regexp
518             # \s+
519             # \:
520             # $atomic_expr_regexp
521             # )
522              
523             my $capture_expr_op_remain_regexp = qr/
524             ^
525             \s*
526             ($atomic_expr_regexp)
527             \s+
528             ($operator_regexp)
529             \s+
530             (.*)
531             $
532             /sxo;
533              
534             #my $capture_expr_if_else_remain_regexp = qr/
535             # ^
536             # \s*
537             # ($atomic_expr_regexp)
538             # \s+ \? \s+
539             # ($atomic_expr_regexp)
540             # \s+ \: \s+
541             # ($atomic_expr_regexp)
542             # \s*
543             # (.*?)
544             # \s*
545             # $
546             # /sx;
547              
548             my $capture_expr_comma_remain_regexp = qr/
549             ^
550             ($expr_regexp)
551             (?:
552             (?: , | => )
553             \s*
554             (.*)
555             )?
556             $
557             /sxo;
558              
559             BEGIN
560             {
561 32     32   414 use Exporter ();
  32         89  
  32         3767  
562              
563 32     32   121 $Template::Sandbox::VERSION = '1.04';
564 32         571 @Template::Sandbox::ISA = qw( Exporter );
565              
566 32         84 @Template::Sandbox::EXPORT = qw();
567 32         127 @Template::Sandbox::EXPORT_OK = qw(
568             inconstant
569             needs_template
570             undef_ok
571             has_args no_args
572             one_arg two_args three_args any_args
573             def_func
574             );
575 32         580476 %Template::Sandbox::EXPORT_TAGS = (
576             function_sugar => [ qw(
577             inconstant
578             needs_template
579             undef_ok
580             has_args no_args
581             one_arg two_args three_args any_args
582             ) ],
583             );
584             }
585              
586             sub _find_local_functions
587             {
588 427     427   667 my ( $self ) = @_;
589              
590 427 100       1373 return( \%functions ) unless ref( $self );
591              
592 396   100     2003 $self->{ local_functions } ||= {};
593 396         995 return( $self->{ local_functions } );
594             }
595              
596             sub register_template_function
597             {
598 388     388 1 766 my $self = shift;
599 388         472 my ( $local_functions );
600              
601 388         968 $local_functions = $self->_find_local_functions();
602              
603 388         1168 while( my $name = shift )
604             {
605 421         462 my ( $func );
606              
607 421         513 $func = shift;
608              
609             # TODO: Carp has errors croaking from here.
610 421 100 66     1390 $self->caller_error(
      100        
611             "Bad template function '$name' to register_template_function(), " .
612             "expected sub ref or 'function_sugar'ed sub ref, got: " .
613             ( ref( $func ) || "'$func'" ) )
614             unless ref( $func ) eq 'ARRAY' or ref( $func ) eq 'CODE';
615              
616             # do local $^W = undef; in calling block to suppress.
617 417 100 66     1466 $self->caller_warning(
618             "Template function '$name' exists, overwriting." )
619             if $^W and $local_functions->{ $name };
620              
621             # If they don't use the function sugar, we assume they're not fussy
622             # TODO: probably safer to error since constant/inconstant shouldn't be assumed
623 417 100       1402 $func = any_args $func if ref( $func ) eq 'CODE';
624              
625 417         2267 $local_functions->{ $name } = $func;
626             }
627             }
628              
629             sub add_template_function
630             {
631 1     1 1 2 my $self = shift;
632              
633 1         5 $self->register_template_function( @_ );
634             }
635              
636             sub unregister_template_function
637             {
638 15     15 1 1046 my $self = shift;
639 15         25 my ( $local_functions );
640              
641 15         44 $local_functions = $self->_find_local_functions();
642              
643 15         57 while( my $name = shift )
644             {
645 20 100 66     90 $self->caller_warning(
646             "Template function '$name' does not exist, cannot be removed." )
647             if $^W and not $local_functions->{ $name };
648              
649 20         608 delete $local_functions->{ $name };
650             }
651             }
652              
653             sub delete_template_function
654             {
655 1     1 1 31 my $self = shift;
656              
657 1         3 $self->unregister_template_function( @_ );
658             }
659              
660             sub _find_local_syntaxes
661             {
662 30     30   59 my ( $self ) = @_;
663              
664 30 100       135 return( \%syntaxes ) unless ref( $self );
665              
666             $self->{ local_syntaxes } ||= {
667             # Faux values to define the auto opcode generation for local syntaxes.
668             # We use negative values to avoid clash with class-level opcodes.
669 26   100     186 '.next_instr' => -(LOCAL_SYNTAX),
670             '.instr_increment' => -1,
671             '.instr' => {},
672             };
673 26   100     117 $self->{ local_token_aliases } ||= {};
674 26         502 return( $self->{ local_syntaxes } );
675             }
676              
677             sub register_template_syntax
678             {
679 24     24 1 5002 my $self = shift;
680 24         41 my ( $local_syntaxes );
681              
682 24         86 $local_syntaxes = $self->_find_local_syntaxes();
683              
684 24         93 while( my $name = shift )
685             {
686 24         44 my ( $syntax );
687              
688 24         40 $syntax = shift;
689              
690             # TODO: Carp has errors when croaking from here.
691 24 100 66     130 $self->caller_error(
692             "Bad template syntax '$name' to register_template_syntax(), " .
693             "expected hash ref, got: " . ( ref( $syntax ) || "'$syntax'" ) )
694             unless ref( $syntax ) eq 'HASH';
695              
696             $self->caller_error( "Missing compile callback for syntax $name" )
697 20 100       68 unless $syntax->{ compile };
698             $self->caller_error( "Missing run callback for syntax $name" )
699 18 100       90 unless $syntax->{ run };
700              
701             # do local $^W = undef; in calling block to suppress.
702 16 100 66     105 $self->caller_warning(
703             "Template syntax '$name' exists, overwriting." )
704             if $^W and $local_syntaxes->{ $name };
705              
706 16         2745 $syntax = { %{$syntax} };
  16         80  
707              
708             # Icky.
709 16         67 $syntax->{ instr } = $local_syntaxes->{ '.next_instr' };
710 16         42 $local_syntaxes->{ '.next_instr' } +=
711             $local_syntaxes->{ '.instr_increment' };
712              
713 16         35 $local_syntaxes->{ $name } = { %{$syntax} };
  16         82  
714 16         265 $local_syntaxes->{ '.instr' }->{ $syntax->{ instr } } = $name;
715             }
716             }
717              
718             sub add_template_syntax
719             {
720 1     1 1 209 my $self = shift;
721              
722 1         6 $self->register_template_syntax( @_ );
723             }
724              
725             sub unregister_template_syntax
726             {
727 6     6 1 2356 my $self = shift;
728 6         16 my ( $local_syntaxes );
729              
730 6         25 $local_syntaxes = $self->_find_local_syntaxes();
731              
732 6         25 while( my $name = shift )
733             {
734 6 100       28 unless( $local_syntaxes->{ $name } )
735             {
736 2 100       17 $self->caller_warning(
737             "Template syntax '$name' does not exist, cannot be removed." )
738             if $^W;
739 2         2691 next;
740             }
741              
742             delete $local_syntaxes->{ '.instr' }->{
743 4         36 $local_syntaxes->{ $name }->{ instr } };
744 4         57 delete $local_syntaxes->{ $name };
745             }
746             }
747              
748             sub delete_template_syntax
749             {
750 1     1 1 52 my $self = shift;
751              
752 1         11 $self->unregister_template_syntax( @_ );
753             }
754              
755             sub get_valid_singular_constructor_param
756             {
757 1476     1476 1 2531 my ( $self ) = @_;
758              
759 1476         5789 return( qw/template cache logger template_root allow_bare_expr
760             ignore_module_dependencies open_delimiter close_delimiter
761             vmethods template_toolkit_compat/ );
762             }
763              
764             sub get_valid_multiple_constructor_param
765             {
766 1476     1476 1 2349 my ( $self ) = @_;
767              
768 1476         4220 return( qw/copy_global_functions template_function template_syntax
769             library/ );
770             }
771              
772             # TODO: implement these constructor options:
773             # -
774             sub new
775             {
776 1476     1476 1 744809 my $this = shift;
777 1476         3398 my ( $self, $class, %param, %valid_singular, %valid_multiple );
778              
779 1476         2824 $self = {};
780 1476   66     7449 $class = ref( $this ) || $this;
781 1476         3863 bless $self, $class;
782              
783 14760         57107 %valid_singular =
784 1476         4066 map { $_ => 1 } $self->get_valid_singular_constructor_param();
785 5904         13516 %valid_multiple =
786 1476         5570 map { $_ => 1 } $self->get_valid_multiple_constructor_param();
787              
788             # Read remaining args.
789 1476         3220 %param = ();
790 1476         4609 while( my $param_name = shift )
791             {
792 417         636 my $param_value = shift;
793              
794 417 100       1142 if( $valid_singular{ $param_name } )
    100          
795             {
796 380         1513 $param{ $param_name } = $param_value;
797             }
798             elsif( $valid_multiple{ $param_name } )
799             {
800 36   100     193 $param{ $param_name } ||= [];
801 36         71 push @{$param{ $param_name }}, $param_value;
  36         171  
802             }
803             else
804             {
805 1         7 $self->caller_error( "Unknown constructor param: '$param_name'" );
806             }
807             }
808              
809 1475         5633 $self->{ phase } = 'initialization';
810 1475         4417 $self->initialize( \%param );
811 1468         3238 $self->{ phase } = 'post-initialization';
812              
813 1468         7941 return( $self );
814             }
815              
816             sub initialize
817             {
818 1475     1475 1 2816 my ( $self, $param ) = @_;
819              
820             # Do this early in case anything needs logging.
821 1475 100       6365 if( exists $param->{ logger } )
822             {
823 3 100       19 $self->{ logger } = $param->{ logger } if $param->{ logger };
824             }
825             else
826             {
827 1472         6443 $self->{ logger } = Log::Any->get_logger();
828             }
829              
830             # For the paranoid, to prevent other code changing them after
831             # we initialize.
832             $self->{ local_functions } = Clone::clone( \%functions )
833 1475 100       24495 if exists $param->{ copy_global_functions };
834              
835 1475 100       3804 if( exists $param->{ template_function } )
836             {
837 19         22 foreach my $arg ( @{$param->{ template_function }} )
  19         44  
838             {
839 20         26 $self->register_template_function( @{$arg} );
  20         59  
840             }
841             }
842 1473 100       3657 if( exists $param->{ template_syntax } )
843             {
844 9         18 foreach my $arg ( @{$param->{ template_syntax }} )
  9         27  
845             {
846 9         17 $self->register_template_syntax( @{$arg} );
  9         40  
847             }
848             }
849 1469 100       3594 if( exists $param->{ library } )
850             {
851 6         11 foreach my $arg ( @{$param->{ library }} )
  6         60  
852             {
853             # Neccessary?
854             # eval "use $arg->[ 0 ];";
855 6         60 $arg->[ 0 ]->export_template_functions( $self,
856 6         544 @{$arg}[ 1..@{$arg} - 1 ] );
  6         14  
857             }
858             }
859              
860             $self->{ ignore_module_dependencies } =
861             $param->{ ignore_module_dependencies }
862 1469 100       3688 if exists $param->{ ignore_module_dependencies };
863              
864 1469 100       3455 if( $param->{ template_toolkit_compat } )
865             {
866             $self->{ open_delimiter } = '[%'
867 5 100       18 unless exists $param->{ open_delimiter };
868             $self->{ close_delimiter } = '%]'
869 5 100       18 unless exists $param->{ close_delimiter };
870             $self->{ allow_bare_expr } = 1
871 5 100       24 unless exists $param->{ allow_bare_expr };
872             $self->{ vmethods } = 1
873 5 100       28 unless exists $param->{ vmethods };
874             }
875              
876             $self->{ open_delimiter } = $param->{ open_delimiter }
877 1469 100       3472 if exists $param->{ open_delimiter };
878             $self->{ close_delimiter } = $param->{ close_delimiter }
879 1469 100       3254 if exists $param->{ close_delimiter };
880             $self->{ allow_bare_expr } = $param->{ allow_bare_expr }
881 1469 100       3586 if exists $param->{ allow_bare_expr };
882             $self->{ vmethods } = $param->{ vmethods }
883 1469 100       6138 if exists $param->{ vmethods };
884              
885             $self->set_cache( $param->{ cache } )
886 1469 100       3306 if exists $param->{ cache };
887             $self->set_template_root( $param->{ template_root } )
888 1469 100       3570 if exists $param->{ template_root };
889             $self->set_template( $param->{ template } )
890 1469 100       3197 if exists $param->{ template };
891              
892 1468         3367 $self->{ vars } = {};
893 1468         4266 $self->{ debug } = {};
894             }
895              
896             sub set_cache
897             {
898 16     16 1 29 my ( $self, $cache ) = @_;
899              
900 16         31 $self->{ cache } = $cache;
901 16         35 delete $self->{ cache_uses_extended_set };
902             }
903              
904             sub _cache_uses_extended_set
905             {
906 8     8   11 my ( $self ) = @_;
907 8         9 my ( $cache );
908              
909             return( $self->{ cache_uses_extended_set } )
910 8 50       25 if exists $self->{ cache_uses_extended_set };
911              
912 8         14 $cache = $self->{ cache };
913 8 100 33     139 return( $self->{ cache_uses_extended_set } = 1 )
      66        
914             if $cache->isa( 'Cache::CacheFactory' ) or
915             ( $cache->can( 'set_takes_named_param' ) and
916             $cache->set_takes_named_param() );
917 4         16 return( $self->{ cache_uses_extended_set } = 0 );
918             }
919              
920             sub set_template_root
921             {
922 4     4 1 30 my ( $self, $dir ) = @_;
923              
924 4         10 $self->{ template_root } = $dir;
925             }
926              
927             sub get_template_candidates
928             {
929 28     28 1 47 my ( $self, $filename, $current_dir ) = @_;
930              
931             return( $self->{ template_root } ?
932 28 100       149 File::Spec->catfile( $self->{ template_root }, $filename ) :
933             $filename );
934             }
935              
936             sub get_include_candidates
937             {
938 14     14 1 18 my ( $self, $filename, $current_dir ) = @_;
939              
940 14 50       165 return( $current_dir ?
941             File::Spec->catfile( $current_dir, $filename ) :
942             $filename );
943             }
944              
945             sub find_template
946             {
947 28     28 1 51 my ( $self, $filename, $current_dir ) = @_;
948 28         38 my ( @candidates );
949              
950 28         75 @candidates = $self->get_template_candidates( $filename, $current_dir );
951 28         64 foreach my $candidate ( @candidates )
952             {
953 28 100       5628 return( $candidate ) if -e $candidate;
954             }
955              
956 2         18 $self->error( "Unable to find matching template from candidates:\n" .
957             join( "\n", @candidates ) );
958             }
959              
960             sub find_include
961             {
962 14     14 1 23 my ( $self, $filename, $current_dir ) = @_;
963 14         13 my ( @candidates );
964              
965 14         31 @candidates = $self->get_include_candidates( $filename, $current_dir );
966              
967 14         37 foreach my $candidate ( @candidates )
968             {
969 14 100       362 return( $candidate ) if -e $candidate;
970             }
971              
972 1         6 $self->error( "Unable to find matching include from candidates:\n" .
973             join( "\n", @candidates ) );
974             }
975              
976             sub cache_key
977             {
978 17     17 1 651 my ( $self, $keys ) = @_;
979 17         36 local $Storable::canonical = 1;
980              
981 17         170 return( Digest::MD5::md5_hex( Storable::nfreeze( $keys ) ) );
982             }
983              
984             sub get_additional_dependencies
985             {
986             # my ( $self ) = @_;
987              
988 1439     1439 1 4598 return( [] );
989             }
990              
991             sub set_template
992             {
993 28     28 1 556 my ( $self, $filename, $defines ) = @_;
994 28         34 my ( $cache_key );
995              
996             #my $start_time = Time::HiRes::time();
997              
998             # Shallow copy is safe, keys/values should only be scalars.
999 28 100       79 $defines = $defines ? { %{$defines} } : {};
  1         4  
1000 28         62 $self->{ defines } = $defines;
1001 28         63 $self->{ special_values } = {};
1002 28         48 delete $self->{ template };
1003              
1004 28         114 $self->{ filename } = $self->find_template( $filename );
1005 26         76 $defines->{ FILENAME } = $self->{ filename };
1006              
1007 26 100       78 if( $self->{ cache } )
1008             {
1009             #my $fetchstart = Time::HiRes::time();
1010             # $defines at this stage includes all unique compile-time
1011             # parameters that effect the final compiled template, this
1012             # is more than just the filename, so we need to generate
1013             # a simple string key from multiple inputs.
1014 8         27 $cache_key = $self->cache_key( $defines );
1015 8         613 $self->{ template } = $self->{ cache }->get( $cache_key );
1016             #warn "Cache fetch: " . $self->{ filename } . " " .
1017             # sprintf( "%.6fs", Time::HiRes::time() - $fetchstart );
1018             }
1019              
1020 26 100       3603 unless( $self->{ template } )
1021             {
1022 22         31 my ( $compiletime );
1023              
1024 22         40 $compiletime = time(); # Before the compile, to be safe.
1025              
1026 22         57 $self->{ dependencies } = $self->get_additional_dependencies();
1027              
1028             # If we're caching, the validity of the cache depends on the
1029             # last-modified of the template module as well as the template
1030             # files, unless we've been told to ignore it.
1031 22 100 100     109 if( $self->{ cache } and not $self->{ ignore_module_dependencies } )
1032             {
1033 2         4 my ( $class_handle );
1034              
1035 2         28 $class_handle = Class::Handle->new( ref( $self ) );
1036 2         22 push @{$self->{ dependencies }},
  4         33  
1037             # TODO: Ew, ugly and non-portable.
1038 4         164 grep { defined( $_ ) }
1039 2         61 map { s/\:\:/\//g; s/$/\.pm/; $INC{ $_ }; }
  4         21  
  4         17  
1040             $class_handle->self_and_super_path();
1041             }
1042              
1043             $self->{ template } =
1044 22         86 $self->_read_template( $self->{ filename }, $defines );
1045              
1046 22         91 $self->_compile_template();
1047              
1048 18 100       78 if( $self->{ cache } )
1049             {
1050             # If they're using Cache::CacheFactory we can make use of
1051             # the dependencies and created at timestamps, if not we
1052             # fall back on the basic Cache::Cache style API.
1053             # TODO: wrap compat cache behaviour with our own dependencies checking.
1054 4 100       14 if( $self->_cache_uses_extended_set() )
1055             {
1056             $self->{ cache }->set(
1057             key => $cache_key,
1058             data => $self->{ template },
1059             dependencies => $self->{ dependencies },
1060 2         13 created_at => $compiletime,
1061             );
1062             }
1063             else
1064             {
1065 2         17 $self->{ cache }->set( $cache_key, $self->{ template } );
1066             }
1067             }
1068             }
1069              
1070             #CORE::warn( "set_template( $filename ) took " .
1071             # sprintf( "%.3f", Time::HiRes::time() - $start_time ) . "s" );
1072             }
1073              
1074             # TODO: split/merge parts from set_template() above.
1075             sub set_template_string
1076             {
1077 1421     1421 1 56044 my ( $self, $template_string, $defines ) = @_;
1078 1421         1874 my ( $cache_key );
1079              
1080             #my $start_time = Time::HiRes::time();
1081              
1082             # Shallow copy is safe, keys/values should only be scalars.
1083 1421 100       4771 $defines = $defines ? { %{$defines} } : {};
  5         22  
1084 1421         2885 $self->{ defines } = $defines;
1085 1421         3476 $self->{ special_values } = {};
1086 1421         2486 delete $self->{ template };
1087              
1088             # Erk. Better way of making this cacheable surely?
1089 1421         4235 $self->{ filename } = 'string:///' . $template_string;
1090 1421         3135 $defines->{ FILENAME } = $self->{ filename };
1091              
1092 1421 100       3726 if( $self->{ cache } )
1093             {
1094             #my $fetchstart = Time::HiRes::time();
1095             # $defines at this stage includes all unique compile-time
1096             # parameters that effect the final compiled template, this
1097             # is more than just the filename, so we need to generate
1098             # a simple string key from multiple inputs.
1099 8         31 $cache_key = $self->cache_key( $defines );
1100 8         445 $self->{ template } = $self->{ cache }->get( $cache_key );
1101             #warn "Cache fetch: " . $self->{ filename } . " " .
1102             # sprintf( "%.6fs", Time::HiRes::time() - $fetchstart );
1103             }
1104              
1105 1421 100       6006 unless( $self->{ template } )
1106             {
1107 1417         1602 my ( $compiletime );
1108              
1109 1417         1940 $compiletime = time(); # Before the compile, to be safe.
1110              
1111 1417         3404 $self->{ dependencies } = $self->get_additional_dependencies();
1112              
1113             # If we're caching, the validity of the cache depends on the
1114             # last-modified of the template module as well as the template
1115             # files, unless we've been told to ignore it.
1116 1417 100 100     4318 if( $self->{ cache } and not $self->{ ignore_module_dependencies } )
1117             {
1118 2         4 my ( $class_handle );
1119              
1120 2         23 $class_handle = Class::Handle->new( ref( $self ) );
1121 2         15 push @{$self->{ dependencies }},
  4         12  
1122             # TODO: Ew, ugly and non-portable.
1123 4         110 grep { defined( $_ ) }
1124 2         45 map { s/\:\:/\//g; s/$/\.pm/; $INC{ $_ }; }
  4         16  
  4         14  
1125             $class_handle->self_and_super_path();
1126             }
1127              
1128             $self->{ template } =
1129 1417         8141 $self->_read_template_from_string( $template_string, $defines );
1130              
1131 1417         3749 $self->_compile_template();
1132              
1133 1318 100       8665 if( $self->{ cache } )
1134             {
1135             # If they're using Cache::CacheFactory we can make use of
1136             # the dependencies and created at timestamps, if not we
1137             # fall back on the basic Cache::Cache style API.
1138             # TODO: wrap compat cache behaviour with our own dependencies checking.
1139 4 100       13 if( $self->_cache_uses_extended_set() )
1140             {
1141             $self->{ cache }->set(
1142             key => $cache_key,
1143             data => $self->{ template },
1144             dependencies => $self->{ dependencies },
1145 2         13 created_at => $compiletime,
1146             );
1147             }
1148             else
1149             {
1150 2         12 $self->{ cache }->set( $cache_key, $self->{ template } );
1151             }
1152             }
1153             }
1154              
1155             #CORE::warn( "set_template( $filename ) took " .
1156             # sprintf( "%.3f", Time::HiRes::time() - $start_time ) . "s" );
1157             }
1158              
1159             sub _error_message
1160             {
1161 159     159   278 my $self = shift;
1162 159         300 my ( $error, $pos );
1163              
1164 159 100       451 $self = {} unless ref( $self ); # Hack for calling as a class method.
1165              
1166 159         377 $error = join( '', @_ );
1167 159 100       708 $error = "Template " . ( $self->{ phase } ? $self->{ phase } . ' ' : '' ) .
1168             "error: $error";
1169 159         263 $pos = $self->{ current_pos };
1170 159 100       386 if( $pos )
1171             {
1172 126         150 my ( $files );
1173              
1174 126 100 66     730 if( $self->{ template } and
    50 66        
1175             ( ref( $self->{ template } ) eq 'HASH' ) and
1176             $self->{ template }->{ files } )
1177             {
1178 23         46 $files = $self->{ template }->{ files };
1179             }
1180             elsif( $self->{ files } )
1181             {
1182 103         219 $files = $self->{ files };
1183             }
1184             else
1185             {
1186 0         0 $files = [];
1187             }
1188 126         655 $error .= " at line $pos->[ 1 ], char $pos->[ 2 ] of " .
1189             "'$files->[ $pos->[ 0 ] ]'";
1190 126 100       343 if( $self->{ pos_stack } )
1191             {
1192 103         124 my ( $first );
1193              
1194 103         147 $first = 1;
1195 103         132 foreach $pos ( @{$self->{ pos_stack }} )
  103         314  
1196             {
1197 104 100       277 $error .= "\n called from " .
1198             "line $pos->[ 1 ], char $pos->[ 2 ] of " .
1199             "'$files->[ $pos->[ 0 ] ]'"
1200             unless $first;
1201 104         540 $first = 0;
1202             }
1203             }
1204             }
1205 159         444 return( $error );
1206             }
1207              
1208             sub log_error
1209             {
1210 139     139 1 229 my ( $self, $message ) = @_;
1211              
1212 139 100       356 return unless ref( $self ); # No logging if class method.
1213 138 100       930 $self->{ logger }->error( $message ) if $self->{ logger };
1214             }
1215              
1216             sub log_warning
1217             {
1218 20     20 1 37 my ( $self, $message ) = @_;
1219              
1220 20 100       61 return unless ref( $self ); # No logging if class method.
1221 19 100       141 $self->{ logger }->warning( $message ) if $self->{ logger };
1222             }
1223              
1224             sub error
1225             {
1226 123     123 1 993 my $self = shift;
1227 123         167 my ( $message );
1228              
1229 123         347 $message = $self->_error_message( @_ );
1230 123         354 $self->log_error( $message );
1231 123         617 $self->fatal_exit( $message );
1232             }
1233              
1234             sub caller_error
1235             {
1236 16     16 1 98 my $self = shift;
1237 16         25 my ( $message );
1238              
1239 16         69 $message = $self->_error_message( @_ );
1240 16         110 $self->log_error( $message );
1241 16         6544 $self->caller_fatal_exit( $message );
1242             }
1243              
1244             sub fatal_exit
1245             {
1246 123     123 1 198 my ( $self, $message ) = @_;
1247              
1248 123         2788 die $message;
1249             }
1250              
1251             sub caller_fatal_exit
1252             {
1253 16     16 1 38 my ( $self, $message ) = @_;
1254              
1255             # TODO: restore once Carp stops dying with:
1256             # Bizarre copy of HASH in sassign at [...]/Carp/Heavy.pm line 96.
1257             # croak $message;
1258 16         7430 die $message;
1259             }
1260              
1261             sub warning
1262             {
1263 15     15 1 878 my $self = shift;
1264 15         21 my ( $message );
1265              
1266 15         138 $message = $self->_error_message( @_ );
1267 15         49 $self->log_warning( $message );
1268 15         187 warn $message;
1269             }
1270              
1271             sub caller_warning
1272             {
1273 5     5 1 57 my $self = shift;
1274 5         9 my ( $message );
1275              
1276 5         26 $message = $self->_error_message( @_ );
1277 5         23 $self->log_warning( $message );
1278 5         130 carp $message;
1279             }
1280              
1281             sub add_var
1282             {
1283 514     514 1 2303 my ( $self, $var, $value ) = @_;
1284              
1285 514 100       1672 $self->caller_error(
1286             "Bad argument to add_var, expected top-level variable name, got: $var"
1287             )
1288             if $var =~ /\./o;
1289              
1290 513         2087 $self->{ vars }->{ $var } = $value;
1291             }
1292              
1293             sub add_vars
1294             {
1295 369     369 1 4465 my ( $self, $vars ) = @_;
1296 369         455 my ( @bad_vars );
1297              
1298 369         2351 $self->caller_error(
1299             "Bad var(s) in add_vars, expected top-level variable name, got: " .
1300             join( ', ', @bad_vars )
1301             )
1302 369 100       534 if @bad_vars = grep /\./o, keys( %{$vars} );
1303              
1304 368         572 foreach my $var ( keys( %{$vars} ) )
  368         882  
1305             {
1306 715         2663 $self->{ vars }->{ $var } = $vars->{ $var };
1307             }
1308             }
1309              
1310             sub _var_value
1311             {
1312 16     16   34 my ( $self, $var ) = @_;
1313              
1314 16         90 return( $self->{ vars }->{ $var } );
1315             }
1316              
1317             sub merge_var
1318             {
1319 10     10 1 20 my ( $self, $var, $value, $ref ) = @_;
1320              
1321 10 100       30 $ref = $self->{ vars } unless $ref;
1322              
1323             #CORE::warn( "merge_var( ",
1324             # Data::Dumper::Dumper( $var ), ", ",
1325             # Data::Dumper::Dumper( $value ), ", ",
1326             # Data::Dumper::Dumper( $ref->{ $var } ), ")\n" );
1327              
1328 10 50 66     55 unless( exists( $ref->{ $var } ) and ref( $value ) and
      66        
      66        
1329             ( ref( $value ) eq 'HASH' or ref( $value ) eq 'ARRAY' ) )
1330             {
1331             #CORE::warn( "Doesn't exist, setting\n" );
1332 6         12 $ref->{ $var } = $value;
1333 6         19 return;
1334             }
1335              
1336 4 100       16 if( ref( $value ) eq 'HASH' )
    50          
1337             {
1338 2         4 foreach my $key ( keys( %{$value} ) )
  2         6  
1339             {
1340 2         9 $self->merge_var( $key, $value->{ $key }, $ref->{ $var } );
1341             }
1342             }
1343             elsif( ref( $value ) eq 'ARRAY' )
1344             {
1345 2 50       9 if( ref( $ref->{ $var } ) eq 'ARRAY' )
1346             {
1347 2         4 push @{$ref->{ $var }}, @{$value};
  2         5  
  2         10  
1348             }
1349             else
1350             {
1351             # Ew, trying to merge array with non-array?
1352             # TODO: error?
1353 0         0 $ref->{ $var } = $value;
1354             }
1355             }
1356             }
1357              
1358             sub merge_vars
1359             {
1360 1     1 1 2 my ( $self, $vars ) = @_;
1361              
1362 1         1 foreach my $var ( keys( %{$vars} ) )
  1         4  
1363             {
1364 4         9 $self->merge_var( $var, $vars->{ $var } );
1365             }
1366             }
1367              
1368             sub clear_vars
1369             {
1370 2     2 1 9 my ( $self ) = @_;
1371              
1372 2         7 $self->{ vars } = {};
1373             }
1374              
1375             sub _escape_string
1376             {
1377 4     4   5 my ( $self, $string ) = @_;
1378              
1379 4         12 $string =~ s/\'/\\\'/go;
1380 4         16 return( $string );
1381             }
1382              
1383             sub _define_value
1384             {
1385 19     19   70 my ( $self, $defines, $define, $default, $quote, $pos ) = @_;
1386 19         25 my ( $value );
1387              
1388             #$self->warning( "replacing define '$define', default '$default', quote is $quote, pos '$pos'" );
1389 19 100       104 if( $self->{ seen_defines }->{ $define }++ )
    100          
    100          
1390             {
1391 1         4 $value = "[recursive define '$define']";
1392             }
1393             elsif( defined( $defines->{ $define } ) )
1394             {
1395 13         25 $value = $defines->{ $define };
1396             }
1397             elsif( defined( $default ) )
1398             {
1399 3         6 $value = $default;
1400             }
1401             else
1402             {
1403 2         8 $value = "[undefined preprocessor define '$define']";
1404             }
1405              
1406 19         64 $value = $self->_replace_defines( $value, $defines );
1407 19         42 $self->{ seen_defines }->{ $define }--;
1408              
1409 19 100       55 $value = "'" . $self->_escape_string( $value ) . "'" if $quote;
1410              
1411 19 100       43 if( defined( $pos ) )
1412             {
1413 18         22 my ( $lines, $definelen, $valuelen );
1414              
1415 18         34 $lines = $value =~ tr/\n//;
1416 18 100       65 $definelen = 3 + ( $quote ? 2 : 0 ) + length( $define ) +
    100          
1417             ( defined( $default ) ? length( $default ) : 0 );
1418 18         111 $valuelen = length( $value );
1419              
1420 18 100       39 if( $lines )
1421             {
1422 1         3 push @{$self->{ offsets }}, [ $pos, $valuelen, -$lines,
  1         5  
1423             $definelen - $valuelen,
1424             ]
1425             }
1426             else
1427             {
1428 17         21 push @{$self->{ offsets }}, [ $pos, $valuelen, 0,
  17         67  
1429             $definelen - $valuelen,
1430             ]
1431             }
1432             }
1433              
1434 19         225 return( $value );
1435             }
1436              
1437             sub _replace_defines
1438             {
1439 1469     1469   2172 my ( $self, $template_content, $defines ) = @_;
1440 1469         1720 my ( $top );
1441              
1442             # Replace any preprocessor defines.
1443 1469 100       3815 unless( $self->{ seen_defines } )
1444             {
1445 1450         2822 $self->{ seen_defines } = {};
1446 1450         2995 $self->{ offsets } = [];
1447 1450         2347 $top = 1;
1448             }
1449 1469         4915 1 while $template_content =~ s/\$\{('?)([A-Z0-9_]+)(?::([^\}]*))?\1\}/
1450 19 100       103 $self->_define_value( $defines, $2, $3, $1,
1451             $top ? pos( $template_content ) : undef )/geox;
1452 1469 100       3148 if( $top )
1453             {
1454 1450         2853 delete $self->{ seen_defines };
1455 1450 100       1698 delete $self->{ offsets } unless @{$self->{ offsets }};
  1450         4884  
1456 1450 100       3875 if( $self->{ offsets } )
1457             {
1458             # pos() gives us position in original string, we need to
1459             # renumber to be position in replaced string.
1460 13         20 my $carry = 0;
1461 13         16 foreach my $offset ( @{$self->{ offsets }} )
  13         35  
1462             {
1463 18         32 $offset->[ 0 ] -= $carry;
1464 18         51 $carry += $offset->[ 3 ];
1465             }
1466             #my $t = $template_content;
1467             #foreach my $offset ( reverse( @{$self->{ offsets }} ) )
1468             #{
1469             # substr( $t, $offset->[ 0 ] + $offset->[ 1 ], 0 ) = "XXX";
1470             # substr( $t, $offset->[ 0 ], 0 ) = "XXX";
1471             #}
1472             #print "Replaced template:\n$t\n";
1473             #use Data::Dumper;
1474             #print "Using offsets: " . Data::Dumper::Dumper( $self->{ offsets } ) . "\n";
1475             }
1476             }
1477              
1478 1469         3455 return( $template_content );
1479             }
1480              
1481             sub _read_template
1482             {
1483 33     33   54 my ( $self, $filename, $defines ) = @_;
1484 33         38 my ( $fh, $template );
1485              
1486 33         38 push @{$self->{ dependencies }}, $filename;
  33         73  
1487              
1488 33         222 $fh = IO::File->new( $filename, '<' );
1489             # TODO: $! can get trashed if $filename is interpolated - investigate
1490             # TODO: is this perl 5.10.0's $! bug, or mine?
1491             # $self->caller_error( "Unable to read $filename: $!" ) unless $fh;
1492 33 50       3171 $self->caller_error( "Unable to read ", $filename, ": $!" ) unless $fh;
1493             {
1494 33         41 local $/;
  33         110  
1495 33         747 $template = <$fh>;
1496             }
1497 33         150 $fh->close;
1498              
1499             # Replace any preprocessor defines.
1500 33         545 $template = $self->_replace_defines( $template, $defines );
1501              
1502 33         181 return( $template );
1503             }
1504              
1505             sub _read_template_from_string
1506             {
1507 1417     1417   2520 my ( $self, $template, $defines ) = @_;
1508              
1509             # Replace any preprocessor defines.
1510 1417         3726 $template = $self->_replace_defines( $template, $defines );
1511              
1512 1417         3690 return( $template );
1513             }
1514              
1515             # Looks for combination of positional and named parameters to a syntax
1516             # token and returns a hashref of named parameters.
1517             # TODO: this is largely obsolete for everything except includes,
1518             # TODO: should be retired in favour of something specialized and faster.
1519             sub _parse_args
1520             {
1521 31     31   67 my ( $self, $args, $type ) = @_;
1522 31         53 my ( $count, %param, @words, @pos_param, @keyword_param, $instr,
1523             @positions, @valid, $syntax );
1524              
1525             # # Heeeello hackery.
1526             # $args = "iterator=\"$1\" set=\"$2\""
1527             # if $type eq 'for' and $args =~ /^(.*) in (.*)$/o;
1528              
1529 31   66     293 $syntax = $self->{ local_syntaxes }->{ $type } || $syntaxes{ $type };
1530              
1531             @positions = $syntax->{ positional_args } ?
1532 31 100       84 @{$syntax->{ positional_args }} : ();
  17         46  
1533 31 100       92 @valid = $syntax->{ valid_args } ? @{$syntax->{ valid_args }} : undef;
  1         4  
1534              
1535 31         56 %param = ();
1536              
1537 31         97 @words = split( /\s+/, $args );
1538             # Merge quoted args.
1539             # TODO: rename instr to in_str for semantic clarity vs "instr"uction.
1540 31         952 $instr = 0;
1541 31         101 for( $count = 0; $count < @words; $count++ )
1542             {
1543 38 100       66 if( $instr )
1544             {
1545 5 100       30 $instr = 0 if $words[ $count ] =~ /\"$/;
1546 5         14 $words[ $count - 1 ] .= ' ' . $words[ $count ];
1547 5         30 @words =
1548             ( @words[ 0..$count - 1 ], @words[ $count + 1..@words - 1 ] );
1549 5         34 $count--;
1550             }
1551             else
1552             {
1553 33 100 100     220 next unless $words[ $count ] =~ /^\"/ or $words[ $count ] =~ /=\"/;
1554 6 100       29 next if $words[ $count ] =~ /\"$/;
1555 2         7 $instr = 1;
1556             }
1557             }
1558              
1559             # Split into positional parameters and keyword paramaters.
1560 31         96 for( $count = 0; $count < @words; $count++ )
1561             {
1562 27 100       102 last if $words[ $count ] =~ /=/;
1563             }
1564              
1565 31 100       99 @pos_param = $count ? @words[ 0..$count - 1 ] : ();
1566 31 100       93 @keyword_param = $count < @words ?
1567             @words[ $count..@words - 1 ] : ();
1568              
1569             # Squidge any "overshoot" positional param onto the final pos param.
1570             # TODO: splice!
1571 31 50       83 @pos_param = ( @pos_param[ 0..@positions - 2 ],
1572             join( ' ', @pos_param[ @positions - 1..@pos_param - 1 ] ) )
1573             if @pos_param > @positions;
1574              
1575 31         44 $count = 0;
1576 31         68 foreach my $word ( @pos_param )
1577             {
1578 17 100       55 $word = $1 if $word =~ /^\"(.*)\"$/;
1579 17         57 $param{ $positions[ $count++ ] } = $word;
1580             }
1581              
1582 31         69 foreach my $word ( @keyword_param )
1583             {
1584 16         18 my ( $keyword, $value );
1585              
1586 16         76 ( $keyword, $value ) = split( /=/, $word, 2 );
1587              
1588 16 100       40 unless( defined( $value ) )
1589             {
1590 1         7 $self->error( "Undefined value for keyword: '$keyword' on " .
1591             "parse_args( $args, $type )" );
1592             }
1593              
1594 15 100       47 $value = $1 if $value =~ /^\"(.*)\"$/;
1595              
1596             # TODO: validate arg names.
1597 15         56 $param{ $keyword } = $value;
1598             }
1599              
1600 30         226 return( { %param } );
1601             }
1602              
1603             sub _compile_template
1604             {
1605 1439     1439   2316 my ( $self ) = @_;
1606 1439         2047 my ( $i, @hunks, @files, @pos_stack, @nest_stack, @compiled, %includes,
1607             %trim, $trim_next, %file_numbers, @define_stack,
1608             $local_syntaxes, $local_token_aliases, $local_syntax_regexp,
1609             $hunk_regexp, $syntax_regexp,
1610             $open_delimiter, $close_delimiter, $open_regexp, $close_regexp );
1611              
1612 1439         3619 @files = ( $self->{ filename } );
1613 1439         4385 %file_numbers = ( $self->{ filename } => 0 );
1614 1439         2802 $self->{ files } = \@files;
1615            
1616             # Stack of what position in which file we're currently at.
1617             @pos_stack = ( [
1618             $file_numbers{ $self->{ filename } }, 1, 1, 0, $self->{ offsets },
1619 1439         5329 ] );
1620 1439         2368 delete $self->{ offsets };
1621             # Stack of what defines are available.
1622 1439         3276 @define_stack = ( $self->{ defines } );
1623             # Stack of unclosed block-level statements.
1624 1439         2066 @nest_stack = ();
1625             # The tokenized/compiled template.
1626 1439         1857 @compiled = ();
1627             # Files we're currently including.
1628 1439         3462 %includes = ( $self->{ filename } => 1 );
1629             # Stuff we're going to trim later.
1630 1439         2176 %trim = ();
1631              
1632 1439   100     6859 $open_delimiter = $self->{ open_delimiter } || '<:';
1633 1439   100     7465 $close_delimiter = $self->{ close_delimiter } || ':>';
1634              
1635 1439         8200 $open_regexp = qr/\Q$open_delimiter\E/;
1636 1439         4953 $close_regexp = qr/\Q$close_delimiter\E/;
1637              
1638 1439   100     8365 $local_token_aliases = $self->{ local_token_aliases } || {};
1639 1439   100     5623 $local_syntaxes = $self->{ local_syntaxes } || {};
1640              
1641             # TODO: class-level syntax aliases
1642             # TODO: split into class/instance versions and unroll to construct time?
1643             # TODO: or generate-on-demand but class/instance copy invalidated on change.
1644             # Egads!
1645 10         75 $local_syntax_regexp = join( ' | ',
1646 1439         3356 map { join( ' \s+ ', split( /\s+/, $_ ) ) }
1647             grep( /^[^\.]/,
1648 1439         2822 keys( %{$local_token_aliases} ), keys( %{$local_syntaxes} ),
  1439         4455  
1649 1439         2233 values( %{$syntaxes{ '.instr' }} ) ) );
1650 1439 100       3685 $local_syntax_regexp = ' | ' . $local_syntax_regexp
1651             if $local_syntax_regexp;
1652              
1653 1439         13776 $syntax_regexp = qr/(
1654             var | expr |
1655             (?:if|unless) | else? \s* (?:if|unless) | else |
1656             end \s* (?:if|unless) |
1657             for(?:each)? | end \s* for(?:each)? | end |
1658             include | end \s* include |
1659             \# |
1660             debug
1661             $local_syntax_regexp
1662             ) \s+
1663             /ix;
1664 1439 100       6269 $syntax_regexp = qr/(?:$syntax_regexp)?/ if $self->{ allow_bare_expr };
1665 1439         17690 $hunk_regexp = qr/^$open_regexp \s*
1666             $syntax_regexp (.*?) \s* $close_regexp (.+)? $/sx;
1667              
1668 1439         10947 @hunks = split( /(?=$open_regexp)/s, $self->{ template }, -1 );
1669 1439         3019 delete $self->{ template };
1670              
1671 1439         3178 $self->{ pos_stack } = \@pos_stack;
1672 1439         2652 $self->{ phase } = 'compile';
1673              
1674             #my ( $dumpme );
1675 1439         4144 for( $i = 0; $i < @hunks; $i++ )
1676             {
1677 3550         4375 my ( $hunk, $pos, $lines, $queue_pos, $last, $hunklen, $hunkstart,
1678             $offset_index );
1679              
1680 3550         5240 $hunk = $hunks[ $i ];
1681              
1682 3550         4571 $pos = [ @{$pos_stack[ 0 ]}[ 0..2 ] ];
  3550         9413  
1683 3550         6544 $self->{ current_pos } = $pos;
1684              
1685 3550 100       30211 if( $hunk =~ $hunk_regexp )
1686             {
1687 2383         2993 my ( $token, $syntax, $args, $rest );
1688              
1689 2383   100     9358 $token = lc( $1 || 'expr' );
1690 2383         4369 $args = $2;
1691 2383         3639 $rest = $3;
1692              
1693             # TODO: still possible? What triggers it?
1694             # error, unclosed token?
1695 2383 50       9603 $self->error( "unexepected $open_delimiter, ",
1696             "possibly unterminated $close_delimiter" )
1697             if $args =~ $open_regexp;
1698              
1699 2383 100       4982 if( defined( $rest ) )
1700             {
1701 989         7849 $hunk =~ s/$close_regexp(?:.*)$/$close_delimiter/s;
1702 989         3285 splice( @hunks, $i, 1, $hunk, $rest );
1703             }
1704              
1705 2383         4785 $token =~ s/\s+/ /go;
1706              
1707 2383 100       5429 if( $token eq 'end' )
1708             {
1709 55 50       140 $self->error( "end found without opening block" )
1710             unless @nest_stack;
1711 55 100       202 $token = ( $nest_stack[ 0 ][ 0 ] eq FOR ) ?
1712             'endfor' : 'endif';
1713             }
1714              
1715 2383 50       6448 $token = $local_token_aliases->{ $token }
1716             if $local_token_aliases->{ $token };
1717 2383 100       5413 $token = $token_aliases{ $token }
1718             if $token_aliases{ $token };
1719 2383   100     9471 $syntax = $local_syntaxes->{ $token } || $syntaxes{ $token };
1720              
1721             # Fudge things a little so that flow-control tokens
1722             # on a line by themselves don't produce a bunch of
1723             # empty lines in the output.
1724             # Are we a zero-width token on a line by itself?
1725 2383 100 100     25119 if( $syntax->{ zero_width } and
      100        
      66        
      100        
1726             $i < @hunks - 1 and
1727             ( ( not @compiled ) or
1728             ( $compiled[ @compiled - 1 ]->[ 0 ] == LITERAL and
1729             $compiled[ @compiled - 1 ]->[ 2 ] =~ /\n\ *$/ ) or
1730             ( $compiled[ @compiled - 1 ]->[ 0 ] == CONTEXT_PUSH ) ) and
1731             $hunks[ $i + 1 ] =~ /^\n\ */ )
1732             {
1733 58         126 $trim_next = 1;
1734             }
1735             else
1736             {
1737 2325         3897 $trim_next = 0;
1738             }
1739              
1740 2383 100 100     20243 if( $syntax->{ compile } )
    100 100        
    100 100        
    100 100        
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
1741             {
1742 14         23 my ( $compiler, $opcode );
1743              
1744 14         57 $args = $self->_parse_args( $args, $token );
1745              
1746 14         29 $compiler = $syntax->{ compile };
1747 14         38 $opcode = $syntax->{ instr };
1748 14         57 $args = $compiler->( $self, $token, $pos, $args );
1749 14 100       132 push @compiled, [ $opcode, $pos, $args ] if defined $args;
1750             }
1751             elsif( $token eq 'debug' )
1752             {
1753 1         5 $args = $self->_parse_args( $args, $token );
1754              
1755 1 50       3 $args = 0 unless keys( %{$args} );
  1         6  
1756              
1757 1         11 push @compiled,
1758             [ DEBUG, $pos, $args ];
1759             }
1760             elsif( $token eq 'expr' or $token eq 'var' )
1761             {
1762 1039         1244 my ( $expr );
1763              
1764 1039         2875 $expr = $self->_compile_expression( $args );
1765              
1766 992 100 100     7912 push @compiled,
1767             [ EXPR, $pos, $expr,
1768             # Void-wrap assign expressions.
1769             ( ( $expr->[ 0 ] == OP_TREE ) and
1770             ( $expr->[ 2 ] eq '=' ) ) ? 1 : 0 ];
1771             }
1772             elsif( $token eq 'if' or $token eq 'unless' )
1773             {
1774 352         454 my ( $expr );
1775              
1776 352         1005 $expr = $self->_compile_expression( $args );
1777 352 100       1361 if( $token ne 'if' )
1778             {
1779 153 100       373 if( $expr->[ 0 ] == LITERAL )
1780             {
1781 24         46 $expr->[ 2 ] = not $expr->[ 2 ];
1782             }
1783             else
1784             {
1785 129         331 $expr = [ UNARY_OP, 'unless', 'not', $expr ];
1786             }
1787             }
1788 352         926 push @compiled,
1789             [ JUMP_IF, $pos, undef, $expr ];
1790             # push @compiled,
1791             # [ JUMP_IF, $pos, undef,
1792             # $self->_compile_expression( $args ),
1793             # $token eq 'if' ? 1 : 0 ];
1794 352         1363 unshift @nest_stack, [ 'if', @compiled - 1 ];
1795             }
1796             elsif( $token eq 'elsif' or $token eq 'elsunless' )
1797             {
1798 316         382 my ( $expr );
1799              
1800 316 100 100     1490 if( ( not @nest_stack ) or
      66        
1801             ( $nest_stack[ 0 ][ 0 ] ne 'if' and
1802             $nest_stack[ 0 ][ 0 ] ne 'elsif' ) )
1803             {
1804 2         6 $self->error( "elsif found without opening if or elsif" );
1805             }
1806             # Closing jump of previous block.
1807 314         743 push @compiled,
1808             [ JUMP, $pos, undef ];
1809              
1810 314         727 $expr = $self->_compile_expression( $args );
1811 314 100       1042 if( $token ne 'elsif' )
1812             {
1813 122 100       278 if( $expr->[ 0 ] == LITERAL )
1814             {
1815 16         40 $expr->[ 2 ] = not $expr->[ 2 ];
1816             }
1817             else
1818             {
1819 106         282 $expr = [ UNARY_OP, 'elsunless', 'not', $expr ];
1820             }
1821             }
1822 314         1134 push @compiled, [ JUMP_IF, $pos, undef, $expr ];
1823             # push @compiled,
1824             # [ JUMP_IF, $pos, undef,
1825             # $self->_compile_expression( $args ),
1826             # $token eq 'elsif' ? 1 : 0 ];
1827             # Now, update jump address of previous if/elsif
1828 314         637 $compiled[ $nest_stack[ 0 ][ 1 ] ][ 2 ] =
1829             @compiled - 1;
1830 314         1115 unshift @nest_stack, [ 'elsif', @compiled - 1 ];
1831             }
1832             elsif( $token eq 'else' )
1833             {
1834 157 100 100     1132 if( ( not @nest_stack ) or
      66        
1835             ( $nest_stack[ 0 ][ 0 ] ne 'if' and
1836             $nest_stack[ 0 ][ 0 ] ne 'elsif' ) )
1837             {
1838 2         6 $self->error( "else found without opening if or elsif" );
1839             }
1840             # Closing jump of previous block.
1841 155         416 push @compiled,
1842             [ JUMP, $pos, undef ];
1843             # Now, update jump address of previous if/elsif
1844 155         316 $compiled[ $nest_stack[ 0 ][ 1 ] ][ 2 ] = @compiled;
1845 155         556 unshift @nest_stack, [ 'else', scalar( @compiled ) ];
1846             }
1847             elsif( $token eq 'endif' or $token eq 'endunless' )
1848             {
1849 349 100 100     2554 if( ( not @nest_stack ) or
      100        
      66        
1850             ( $nest_stack[ 0 ][ 0 ] ne 'if' and
1851             $nest_stack[ 0 ][ 0 ] ne 'elsif' and
1852             $nest_stack[ 0 ][ 0 ] ne 'else' ) )
1853             {
1854 2         7 $self->error(
1855             "endif found without opening if, elsif or else" );
1856             }
1857              
1858             # Update jump address of previous if/elsif
1859 347 100       977 $compiled[ $nest_stack[ 0 ][ 1 ] ][ 2 ] = @compiled
1860             unless $nest_stack[ 0 ][ 0 ] eq 'else';
1861              
1862 347         746 while( @nest_stack )
1863             {
1864 816         1109 my $last = shift @nest_stack;
1865              
1866 816 100 66     2681 if( $last->[ 0 ] eq 'if' )
    50          
1867             {
1868             # It's our opening if, stop popping.
1869 347         1115 last;
1870             }
1871             elsif( $last->[ 0 ] eq 'elsif' or $last->[ 0 ] eq 'else' )
1872             {
1873             # Need to update the jump address of the closing
1874             # jump of the block _prior_ to this elsif/else.
1875 469         1777 $compiled[ $last->[ 1 ] - 1 ][ 2 ] = @compiled;
1876             }
1877             else
1878             {
1879             # "cannot happen".
1880 0         0 $self->error(
1881             "nesting stack appears to be corrupted" );
1882             }
1883             }
1884             }
1885             elsif( $token eq 'for' )
1886             {
1887 65         163 my ( $iterator, $set );
1888              
1889             # TODO: syntax checking/error check needed here.
1890 65         442 ( $iterator, $set ) = $args =~ /^(.*) in (.*)$/io;
1891              
1892 65         245 push @compiled,
1893             [ FOR, $pos, undef, $iterator,
1894             $self->_compile_expression( $set ),
1895             1 ];
1896 65         336 unshift @nest_stack, [ FOR, @compiled - 1 ];
1897             }
1898             elsif( $token eq 'endfor' )
1899             {
1900 62 100 100     349 if( ( not @nest_stack ) or
1901             $nest_stack[ 0 ][ 0 ] ne FOR )
1902             {
1903 2         6 $self->error(
1904             "endfor found without opening for" );
1905             }
1906              
1907 60         115 my $last = shift @nest_stack;
1908              
1909             # Grab our iterator and set from the opening for
1910             # TODO: needed anymore? run grabs it from for-stack.
1911 60         265 push @compiled,
1912             [ END_FOR, $pos, $last->[ 1 ] + 1,
1913             $compiled[ $last->[ 1 ] ][ 3 ],
1914             $compiled[ $last->[ 1 ] ][ 4 ] ];
1915             # Update jump address of opening for.
1916 60         223 $compiled[ $last->[ 1 ] ][ 2 ] = @compiled;
1917             }
1918             elsif( $token eq 'include' )
1919             {
1920 16         18 my ( $filename, $inc_template, @inc_hunks, %defines );
1921              
1922             # We support var renaming:
1923             # ie: <: include pagerwidget offset=param.offset
1924             # total=results.__size__ pagesize=param.n :>
1925              
1926 16         48 $args = $self->_parse_args( $args, 'include' );
1927              
1928             # Extract the filename.
1929             # If the filename is empty-string then we ignore the
1930             # include statement, allowing us to do things like
1931             # <: include ${DEFINE:} :> without knowing if the define
1932             # exists or not.
1933 15 100       47 if( $filename = $args->{ filename } )
1934             {
1935 14         18 my ( $volume, $current_dir );
1936              
1937 14         26 delete $args->{ filename };
1938              
1939             ( $volume, $current_dir ) = File::Spec->splitpath(
1940 14         228 $define_stack[ 0 ]->{ FILENAME } );
1941             # Make sure volume is part of the current dir so
1942             # windows doesn't choke.
1943 14         107 $current_dir = File::Spec->catpath(
1944             $volume, $current_dir, '' );
1945              
1946 14         16 %defines = %{$define_stack[ 0 ]};
  14         50  
1947              
1948 14         28 $self->{ defines } = \%defines;
1949 14         22 unshift @define_stack, \%defines;
1950              
1951             # Parse out any defines.
1952 14         17 foreach my $key (
  8         22  
1953 14         39 grep { $_ eq uc( $_ ) } keys( %{$args} ) )
1954             {
1955 3         7 $defines{ $key } = $args->{ $key };
1956 3         6 delete $args->{ $key };
1957             }
1958 5         12 $args = { map
1959 14         21 { $_ => $self->_compile_expression( $args->{ $_ } ) }
1960 14         22 keys( %{$args} ) };
1961 14 100       25 $args = 0 unless keys( %{$args} );
  14         41  
1962              
1963 14         38 $filename = $self->find_include( $filename, $current_dir );
1964              
1965 13 100       48 $self->error( "recursive include of $filename" )
1966             if $includes{ $filename };
1967              
1968 11         16 $defines{ FILENAME } = $filename;
1969              
1970 11         26 $includes{ $filename } = 1;
1971 11         23 $inc_template =
1972             $self->_read_template( $filename, \%defines );
1973 11         55 $inc_template =~ s/\n$//;
1974 11         65 @inc_hunks = split( /(?=<:)/, $inc_template, -1 );
1975 11         20 $inc_template = 0;
1976              
1977 11         39 splice( @hunks, $i + 1, 0,
1978             @inc_hunks, '<: endinclude :>' );
1979              
1980 11         30 push @compiled,
1981             [ CONTEXT_PUSH, $pos, $args ];
1982 11 100       32 unless( exists( $file_numbers{ $filename } ) )
1983             {
1984 10         20 $file_numbers{ $filename } = @files;
1985 10         15 push @files, $filename;
1986             }
1987             $queue_pos = [ $file_numbers{ $filename }, 1, 1, 0,
1988 11         33 $self->{ offsets } ];
1989 11         48 delete $self->{ offsets };
1990             }
1991             }
1992             elsif( $token eq 'endinclude' )
1993             {
1994             # <: endinclude :> is a faux-token, it never gets read
1995             # in from a template (isn't valid syntax even), but gets
1996             # inserted to mark the end of the inserted hunks from
1997             # an <: include :>
1998             # "cannot happen".
1999 10 50       21 $self->error( "endinclude found while not within an include" )
2000             unless @pos_stack;
2001              
2002 10         13 my $last = shift @pos_stack;
2003 10         23 delete $includes{ $files[ $last->[ 0 ] ] };
2004 10         11 shift @define_stack;
2005 10         19 $self->{ defines } = $define_stack[ 0 ];
2006 10         24 push @compiled,
2007             [ CONTEXT_POP, $pos ];
2008 10         53 next; # So we don't update pos with this faux-token.
2009             }
2010             elsif( $token eq '#' )
2011             {
2012             # We're a comment, don't compile it.
2013             }
2014             else
2015             {
2016             # Shouldn't be possible to get through the regexp to this.
2017 0         0 $self->error( "unrecognised token ($token)" );
2018             }
2019             }
2020             else
2021             {
2022             # We're a literal unless we're a malformed token
2023 1167 100       4910 if( $hunk =~ /^$open_regexp/ )
2024             {
2025             # Trim bits after the close token if there is one,
2026             # makes a clearer error message.
2027 42         841 $hunk =~ s/($close_regexp).*$/$1/;
2028 42         265 $self->error( "unrecognised token ($hunk)" );
2029             }
2030 1125 50       2378 if( length( $hunk ) )
2031             {
2032 1125 100       2363 $trim{ @compiled } = 1 if $trim_next;
2033 1125         3200 push @compiled, [ LITERAL, $pos, $hunk ];
2034             }
2035 1125         2341 $trim_next = 0;
2036             }
2037              
2038             # +--------------------+------------------------+-----------------------------+
2039             # | define > | nl | !nl |
2040             # +--- hunk v ---------+------------------------+-----------------------------+
2041             # | !nl | not possible (1) | offsets: char |
2042             # | nl after defines | offsets: nl, !char | offsets: nl, !char (no-op) |
2043             # | nl before defines | offset: nl | offsets: char |
2044             # | | char: | |
2045             # | | chars between nl & | |
2046             # | | start of define (2) + | |
2047             # | | original define len + | |
2048             # | | chars after define | |
2049             # | nl between defines | treat in reverse series as after/before |
2050             # +--------------------+------------------------+-----------------------------+
2051             #
2052             # TODO: (1) define spans hunks? (add test case!)
2053             # TODO: define spans defines? (add test case!)
2054             # (2) characters is "fudged count", there may be other defines there. :(
2055             #
2056             # Detection:
2057             # nl in define: nl offset != 0
2058             # nl in hunk: nl in final hunk != total nl in offsets
2059             #
2060              
2061              
2062             # Update pos.
2063 3439         4775 $hunklen = length( $hunk );
2064 3439         4605 $pos = $pos_stack[ 0 ];
2065 3439         5034 $hunkstart = $pos->[ 3 ];
2066 3439         4884 $pos->[ 3 ] += $hunklen;
2067              
2068             #use Data::Dumper;
2069             #print "After hunk: xxx${hunk}xxx\nPos is: " . Data::Dumper::Dumper( $pos ) . "\n";
2070              
2071             # Do we have offsets, and have we just passed one?
2072 3439         4275 $offset_index = -1;
2073 3439   100     9495 while( $pos->[ 4 ] and $offset_index < @{$pos->[ 4 ]} - 1 and
  36   100     236  
2074             $pos->[ 4 ]->[ $offset_index + 1 ]->[ 0 ] <= $pos->[ 3 ] )
2075             {
2076 18         23 my ( $offset );
2077              
2078 18         23 $offset_index++;
2079 18         29 $offset = $pos->[ 4 ]->[ $offset_index ];
2080             # Replace any newlines in the section that was the contents
2081             # of a define, this is so that they don't count towards line
2082             # counts or finding the "most recent newline" for character
2083             # position counts.
2084             # This is inelegant but much simpler (and possibly faster)
2085             # than trying to compensate and find the "right" newline
2086             # to count from, especially since defines containing newlines
2087             # are hopefully a corner-case.
2088             #print "Offset index: $offset_index\nOffset: " . Data::Dumper::Dumper( $offset ) . "\n";
2089             #print "substr( hunk, " . ( $offset->[ 0 ] - $hunkstart ) . ", " . ( $offset->[ 1 ] ) . " )\n" if $offset->[ 2 ];
2090              
2091 18 100       82 substr( $hunk, $offset->[ 0 ] - $hunkstart, $offset->[ 1 ] ) =~
2092             s/\n/ /go
2093             if $offset->[ 2 ];
2094             }
2095              
2096             # $lines = () = $hunk =~ /\n/g;
2097             # $lines = $#{ [ $hunk =~ /\n/g ] } + 1;
2098 3439         6308 $lines = $hunk =~ tr/\n//;
2099 3439 100       6726 if( $lines )
2100             {
2101 106         152 $pos->[ 1 ] += $lines;
2102 106 100       416 $pos->[ 2 ] =
2103             ( $hunk =~ /\n(.+)\z/mo ) ? ( length( $1 ) + 1 ) : 1;
2104             }
2105             else
2106             {
2107 3333         5201 $pos->[ 2 ] += $hunklen;
2108             }
2109              
2110 3439 100       7356 if( $offset_index != -1 )
2111             {
2112 15         18 my ( @offsets, $nlpos );
2113              
2114 15         24 @offsets = splice( @{$pos->[ 4 ]}, 0, $offset_index + 1 );
  15         45  
2115 15 100       21 $pos->[ 4 ] = undef unless @{$pos->[ 4 ]};
  15         48  
2116              
2117 15 100       44 $nlpos = $lines ? ( $pos->[ 3 ] - $pos->[ 2 ] ) : 0;
2118              
2119 15         37 foreach my $offset ( @offsets )
2120             {
2121             # Don't apply the offset if it was before the final
2122             # non-define newline
2123 18 100       56 next if $offset->[ 0 ] < $nlpos;
2124             #use Data::Dumper;
2125             #print "Applying offset: " . Data::Dumper::Dumper( $offset ) . "\n";
2126             # $pos->[ 1 ] += $offset->[ 2 ];
2127 12         45 $pos->[ 2 ] += $offset->[ 3 ];
2128             }
2129             }
2130              
2131 3439 100       15528 unshift @pos_stack, $queue_pos if $queue_pos;
2132             }
2133              
2134 1338 100       2877 $self->error( "unterminated if or for block" ) if @nest_stack;
2135              
2136             # "cannot happen".
2137 1336 50       2927 $self->error( "include stack not empty, corrupted?" ) if @pos_stack > 1;
2138              
2139             # TODO: scan for undef jump addresses.
2140              
2141 1336         4168 foreach my $addr ( keys( %trim ) )
2142             {
2143             # "cannot happen".
2144 54 50       151 $self->error( "trim on non-literal, trim-stack corrupted?" )
2145             unless $compiled[ $addr ]->[ 0 ] == LITERAL;
2146 54         215 $compiled[ $addr ]->[ 2 ] =~ s/^\n//o;
2147             }
2148              
2149             # We're done.
2150             # $self->{ template } = {
2151             # program => [ @compiled ],
2152             # files => [ @files ],
2153             # };
2154             $self->{ template } = {
2155 1336         5527 program => \@compiled,
2156             files => \@files,
2157             };
2158 1336         3959 $self->_optimize_template();
2159             $self->{ template }->{ last_instr } =
2160 1336         1720 @{$self->{ template }->{ program }} - 1;
  1336         4178  
2161              
2162 1336         2865 delete $self->{ current_pos };
2163 1336         2103 delete $self->{ pos_stack };
2164 1336         2084 delete $self->{ files };
2165 1336         12108 delete $self->{ phase };
2166              
2167             #$dumpme = 1;
2168             #use CGI;
2169             #print CGI->header('text/plain');
2170              
2171             #if( $dumpme )
2172             #{
2173             #print "\n----\n" . $self->dumpable_template() . "----\n";
2174             #exit(0);
2175             #}
2176             }
2177              
2178             sub _optimize_template
2179             {
2180 1336     1336   2531 my ( $self ) = @_;
2181 1336         2021 my ( $program, @nest_stack, %deletes, %jump_targets, @loop_blocks,
2182             $value );
2183              
2184             # my ( @function_table, %function_index );
2185              
2186             # Optimization pass:
2187             # TODO: unroll constant low-count fors?
2188              
2189 1336         2589 $program = $self->{ template }->{ program };
2190              
2191             # Fold constant expr into constant instr.
2192 1336         2043 for( my $i = 0; $i < @{$program}; $i++ )
  4711         11294  
2193             {
2194             # Are we an EXPR instr and is our expr a LITERAL expr?
2195 3375 100 100     12426 next unless $program->[ $i ]->[ 0 ] == EXPR and
2196             $program->[ $i ]->[ 2 ]->[ 0 ] == LITERAL;
2197              
2198             #warn "Folding literal expr $i (val: " . $program->[ $i ]->[ 2 ]->[ 2 ] . ") (orig: " . $program->[ $i ]->[ 2 ]->[ 1 ] . ") into literal instr.";
2199              
2200 212         353 $program->[ $i ]->[ 0 ] = LITERAL;
2201 212         675 $program->[ $i ]->[ 2 ] = $program->[ $i ]->[ 2 ]->[ 2 ];
2202             }
2203              
2204              
2205             # Fold constant JUMP_IF into JUMP or delete.
2206 1336         2414 %deletes = ();
2207 1336         2123 for( my $i = 0; $i < @{$program}; $i++ )
  4711         10256  
2208             {
2209 3375 100 100     10916 next unless $program->[ $i ]->[ 0 ] == JUMP_IF and
2210             $program->[ $i ]->[ 3 ]->[ 0 ] == LITERAL;
2211              
2212 94         279 $value = $self->_eval_expression( $program->[ $i ]->[ 3 ], 1 );
2213             # $value = not $value if $program->[ $i ]->[ 4 ];
2214              
2215 94 100       194 if( $value )
2216             {
2217             # Always true, remove the JUMP.
2218             #warn "Folding constant JUMP_IF into no-op.";
2219 48         141 $deletes{ $i } = 1;
2220             }
2221             else
2222             {
2223             # Always false, fold it into a JUMP.
2224             #warn "Folding constant JUMP_IF into JUMP.";
2225 46         195 $program->[ $i ] = [ JUMP, $program->[ $i ]->[ 1 ],
2226             $program->[ $i ]->[ 2 ] ];
2227             }
2228             }
2229 1336 100       3081 $self->_delete_instr( $program, keys( %deletes ) ) if %deletes;
2230              
2231              
2232             # Trim empty context pushes (TODO: that have no assigns in top level)
2233 1336         2175 %deletes = ();
2234 1336         1950 @nest_stack = ();
2235 1336         1889 for( my $i = 0; $i < @{$program}; $i++ )
  4663         10297  
2236             {
2237 3327 100       7188 if( $program->[ $i ]->[ 0 ] == CONTEXT_PUSH )
2238             {
2239 10         19 unshift @nest_stack, $i;
2240 10 100       29 $deletes{ $i } = 1 unless $program->[ $i ]->[ 2 ];
2241 10         19 next;
2242             }
2243 3317 100       7813 if( $program->[ $i ]->[ 0 ] == CONTEXT_POP )
2244             {
2245 10         10 my ( $match );
2246              
2247 10         12 $match = shift @nest_stack;
2248 10 100       30 $deletes{ $i } = 1 if $deletes{ $match };
2249 10         17 next;
2250             }
2251             }
2252 1336 100       2819 $self->_delete_instr( $program, keys( %deletes ) ) if %deletes;
2253              
2254              
2255             # Now scan for adjacent literals to merge where the second
2256             # isn't a jump target.
2257 1336         1971 %deletes = ();
2258              
2259             # For speed, prebuild a list of all jump targets.
2260 1336         1858 %jump_targets = ();
2261 1336         1680 foreach my $line ( @{$program} )
  1336         2760  
2262             {
2263 3313 100 100     24254 next unless $line->[ 0 ] == JUMP or
      100        
      100        
2264             $line->[ 0 ] == JUMP_IF or
2265             $line->[ 0 ] == FOR or
2266             $line->[ 0 ] == END_FOR;
2267 1202         3235 $jump_targets{ $line->[ 2 ] } = 1;
2268             }
2269              
2270             # Now scan for adjacent literals.
2271 1336         2068 for( my $i = @{$program} - 1; $i > 0; $i-- )
  1336         5016  
2272             {
2273             # Are both ourself and our previous instr a literal?
2274 1981 100 100     8634 next if $program->[ $i ]->[ 0 ] != LITERAL or
2275             $program->[ $i - 1 ]->[ 0 ] != LITERAL;
2276              
2277             # Do any jumps lead to the second literal?
2278 83 100       256 next if $jump_targets{ $i };
2279              
2280             #warn "Merging literal $i to previous.";
2281             #warn "Merging literals [" . $program->[ $i - 1 ]->[ 2 ] . "] and [" . $program->[ $i ]->[ 2 ] . "]";
2282              
2283             # Ok, no reason for us to remain apart, let's get married.
2284 51         148 $program->[ $i - 1 ]->[ 2 ] .= $program->[ $i ]->[ 2 ];
2285 51         261 $deletes{ $i } = 1;
2286             }
2287             #warn "Literal merges: " . scalar( keys( %deletes ) );
2288 1336 100       3157 $self->_delete_instr( $program, keys( %deletes ) ) if %deletes;
2289              
2290             # Look for loops that make no use of special loop vars.
2291 1336         2069 @loop_blocks = ();
2292 1336         1908 for( my $i = 0; $i < @{$program}; $i++ )
  4598         10502  
2293             {
2294             # Are we a for statement?
2295 3262 100       8159 next if $program->[ $i ]->[ 0 ] != FOR;
2296 60         242 push @loop_blocks,
2297             [ $i, $program->[ $i ]->[ 2 ], $program->[ $i ]->[ 3 ] ];
2298             }
2299             # TODO: this should be moved into the above loop to keep it single-pass.
2300 1336         5058 foreach my $block ( @loop_blocks )
2301             {
2302 60         70 my ( $special_vars_needed, $line );
2303              
2304 60         85 $special_vars_needed = 0;
2305 60         197 FORBLOCK: for( my $i = $block->[ 0 ] + 1; $i < $block->[ 1 ]; $i++ )
2306             {
2307 128         151 my ( @exprs );
2308              
2309 128         166 $line = $program->[ $i ];
2310 128 100       534 if( $line->[ 0 ] == EXPR )
    100          
    100          
    100          
2311             {
2312 47         87 @exprs = ( $line->[ 2 ] );
2313             }
2314             elsif( $line->[ 0 ] == FOR )
2315             {
2316 3         6 @exprs = ( $line->[ 4 ] );
2317             }
2318             elsif( $line->[ 0 ] == JUMP_IF )
2319             {
2320 4         9 @exprs = ( $line->[ 3 ] );
2321             }
2322             elsif( $line->[ 0 ] == CONTEXT_PUSH )
2323             {
2324 1         2 @exprs = values( %{$line->[ 2 ]} );
  1         3  
2325             }
2326              
2327 128 100       422 next unless @exprs;
2328              
2329 55         137 while( my $expr = shift( @exprs ) )
2330             {
2331 63         64 my ( $type );
2332              
2333 63         81 $type = $expr->[ 0 ];
2334 63 100       150 if( $type == VAR )
    100          
    100          
    100          
    50          
2335             {
2336 54         49 my ( $segments );
2337              
2338 54         83 $segments = $expr->[ 2 ];
2339             # Needs to have two or more segments.
2340 54 100       186 next unless $expr->[ 4 ] > 0;
2341             # Top stem isn't our loop var, we're not interested.
2342 34 100       92 next unless $segments->[ 0 ] eq $block->[ 2 ];
2343              
2344             # OK, it's refering to our loop var, is it a special?
2345 33 100 100     199 if( ref( $segments->[ 1 ] ) or
2346             exists( $special_values_names{ $segments->[ 1 ] } ) )
2347             {
2348             # Yes, it's either a special or an inconstant
2349             # expression subscript that we can't rule out
2350             # as evaluating to a special at runtime.
2351 31         36 $special_vars_needed = 1;
2352 31         93 last FORBLOCK;
2353             }
2354             }
2355             elsif( $type == OP_TREE )
2356             {
2357 1         4 push @exprs, $expr->[ 3 ], $expr->[ 4 ];
2358             }
2359             elsif( $type == UNARY_OP )
2360             {
2361 1         3 push @exprs, $expr->[ 3 ];
2362             }
2363             elsif( $type == FUNC )
2364             {
2365 6         10 push @exprs, @{$expr->[ 3 ]};
  6         26  
2366             }
2367             elsif( $type == METHOD )
2368             {
2369 1         1 push @exprs, @{$expr->[ 4 ]};
  1         4  
2370             }
2371             }
2372             }
2373 60 100       374 $program->[ $block->[ 0 ] ]->[ 5 ] = 0 unless $special_vars_needed;
2374             }
2375              
2376              
2377             # # walk program looking for functions, adding to function table.
2378             # # NOTE: turned out to not make a difference in run-time, but may revisit.
2379             # @function_table = ();
2380             # %function_index = ();
2381             # foreach my $line ( @{$program} )
2382             # {
2383             # my ( $op, @op_queue );
2384             # @op_queue = ();
2385             #
2386             # if( $line->[ 0 ] == EXPR )
2387             # {
2388             # push @op_queue, $line->[ 2 ];
2389             # }
2390             # elsif( $line->[ 0 ] == JUMP_IF )
2391             # {
2392             # push @op_queue, $line->[ 3 ];
2393             # }
2394             # elsif( $line->[ 0 ] == URL )
2395             # {
2396             # push @op_queue, %{$line->[ 2 ]};
2397             # }
2398             # elsif( $line->[ 0 ] == FOR )
2399             # {
2400             # push @op_queue, $line->[ 4 ];
2401             # }
2402             # elsif( $line->[ 0 ] == CONTEXT_PUSH )
2403             # {
2404             # push @op_queue, values( %{$line->[ 2 ]} );
2405             # }
2406             # while( defined( $op = shift( @op_queue ) ) )
2407             # {
2408             # next if not ref( $op ) or $op->[ 0 ] == VAR or
2409             # $op->[ 0 ] == LITERAL or $op->[ 0 ] == TEMPLATE;
2410             # if( $op->[ 0 ] == OP_TREE )
2411             # {
2412             # push @op_queue, $op->[ 3 ], $op->[ 4 ];
2413             # next;
2414             # }
2415             # if( $op->[ 0 ] == UNARY_OP )
2416             # {
2417             # push @op_queue, $op->[ 3 ];
2418             # next;
2419             # }
2420             # if( $op->[ 0 ] == METHOD )
2421             # {
2422             # push @op_queue, @{$op->[ 4 ]};
2423             # next;
2424             # }
2425             # $self->error( "Unknown EXPR opcode: " . $op->[ 0 ] .
2426             # " in function table construction." )
2427             # unless $op->[ 0 ] == FUNC;
2428             #
2429             ##warn "Looking at op " . _tinydump( $op );
2430             ##warn " Is function $op->[ 2 ]().";
2431             # if( not $function_index{ $op->[ 2 ] } )
2432             # {
2433             # push @function_table, $op->[ 2 ];
2434             # $function_index{ $op->[ 2 ] } = $#function_table;
2435             # }
2436             # $op->[ 2 ] = $function_index{ $op->[ 2 ] };
2437             ##warn " Replaced with $op->[ 2 ].";
2438             # push @op_queue, @{$op->[ 3 ]};
2439             # }
2440             # }
2441             # $template->{ function_table } = [ @function_table ];
2442             }
2443              
2444             # Warning, pass-by-ref: modifies $program.
2445             sub _delete_instr
2446             {
2447 79     79   171 my ( $self, $program, @addrs ) = @_;
2448 79         106 my ( %renumbers, $instr, $num, $lastnum, $lastoffset, $offset, $numaddr );
2449              
2450             #warn "** Deleting instr: " . join( ', ', @addrs ) . ".";
2451             #warn "-- Pre:\n" . $self->dumpable_template();
2452              
2453             # Delete all the stuff we've marked for deletion.
2454              
2455             # First we need to sort the deletes.
2456 79         233 @addrs = sort { $a <=> $b } @addrs;
  43         107  
2457              
2458             # Then we delete the instructions from last to first.
2459             # (To avoid renumbering issues).
2460 79         150 foreach my $addr ( reverse( @addrs ) )
2461             {
2462 113         132 splice( @{$program}, $addr, 1 );
  113         442  
2463             }
2464              
2465             #warn "-- Deleted:\n" . $self->dumpable_template();
2466              
2467             # Now we need to renumber any jump and loop targets affected.
2468 79         138 %renumbers = ();
2469 79         116 $lastnum = $lastoffset = 0;
2470 79         117 $numaddr = @addrs - 1;
2471 79         167 foreach my $line ( @{$program} )
  79         169  
2472             {
2473 435 100 100     3028 next unless ( $instr = $line->[ 0 ] ) == JUMP or
      100        
      100        
2474             $instr == JUMP_IF or
2475             $instr == FOR or
2476             $instr == END_FOR;
2477              
2478 133 100       319 if( exists( $renumbers{ $num = $line->[ 2 ] } ) )
2479             {
2480 16         29 $line->[ 2 ] = $renumbers{ $num };
2481 16         30 next;
2482             }
2483              
2484             # This contraption takes advantages of the fact that jumps
2485             # tend to have fairly local targets to other local jumps'
2486             # targets, rather than searching from the start of the
2487             # template each time.
2488 117 100       209 if( $lastnum <= $num )
2489             {
2490             #use Data::Dumper;
2491             #print "Jump target: $num.\nDeleted: ", Data::Dumper::Dumper( \@addrs ), "\n";
2492             # This jump is forwards from our last, search forwards.
2493 90         208 for( $offset = $lastoffset; $offset <= $numaddr; $offset++ )
2494             {
2495             #print " Offset is $offset, addrs[ $offset ] is $addrs[ $offset ]\n";
2496 95 100       264 last if $addrs[ $offset ] >= $num;
2497             }
2498             }
2499             else
2500             {
2501             # This jump is before our last, search backwards.
2502 27         79 for( $offset = $lastoffset; $offset > 0; $offset-- )
2503             {
2504 26 100       87 last if $addrs[ $offset - 1 ] < $num;
2505             }
2506             }
2507 117         152 $lastnum = $num;
2508 117         211 $lastoffset = $offset;
2509              
2510             # Cache the result, if-elsif-else will have lots of the same targets.
2511 117         295 $renumbers{ $num } = ( $line->[ 2 ] -= $offset );
2512             }
2513              
2514             #warn "-- Renumbered:\n" . $self->dumpable_template();
2515             }
2516              
2517             sub _compile_expression
2518             {
2519 3616     3616   5950 my ( $self, $expression ) = @_;
2520 3616         4034 my ( @top_level, $highest_weight, $highest_pos );
2521              
2522 3616         7250 $expression =~ s/^\s+//;
2523 3616         6989 $expression =~ s/\s+$//;
2524              
2525 3616 100       37462 $self->error( "Not a well-formed expression: $expression" )
2526             unless $expression =~ /^$expr_regexp$/so;
2527              
2528 3610         22966 while( $expression =~ $capture_expr_op_remain_regexp )
2529             {
2530             # $lhs = $1;
2531             # $op = $2;
2532             # $rhs = $3;
2533 320         878 push @top_level, $1, $2;
2534 320         1591 $expression = $3;
2535             }
2536              
2537 3610 100       9213 return( $self->_build_op_tree( [ @top_level, $expression ] ) )
2538             if @top_level;
2539              
2540             # Not a compound statement, must be atomic.
2541              
2542             # Is it a unary op?
2543 3291 100       16586 if( my ( $op, $subexpr ) =
2544             $expression =~ $capture_unary_operator_regexp )
2545             {
2546 45         119 $subexpr = $self->_compile_expression( $subexpr );
2547              
2548             # Fold constant values.
2549 45 100       187 return( [ LITERAL, $expression,
2550             $self->_eval_unary_op( $op, $subexpr ) ] )
2551             if $subexpr->[ 0 ] == LITERAL;
2552              
2553 21         86 return( [ UNARY_OP, $expression, $op, $subexpr ] );
2554             }
2555              
2556             # Is it a bracketed expression?
2557             # TODO: Do I care at this point if it's matching?
2558             # return( $self->_compile_expression( substr( $expression, 1, -1 ) ) )
2559             # if $expression =~ /^$matching_round_brackets_regexp$/so;
2560 3246 100       7524 return( $self->_compile_expression( $1 ) )
2561             if $expression =~ /^\((.*)\)$/so;
2562              
2563             # A literal number
2564 3241 100       11830 return( [ LITERAL, $expression, $expression, 0 ] )
2565             if $expression =~ /^$literal_number_regexp$/so;
2566              
2567             # A literal string
2568 2828 100       10699 if( $expression =~ /^$single_quoted_text_regexp$/so )
2569             {
2570 1012         1434 my ( $string );
2571              
2572             # Strip leading/trailing ' and unescape backslashed characters.
2573 1012         2208 $string = substr( $expression, 1, -1 );
2574 1012         1508 $string =~ s/\\(.)/$1/go;
2575 1012         5902 return( [ LITERAL, $expression, $string, 0 ] );
2576             }
2577              
2578             # A variable or chained construct (including functions)
2579 1816 50       13747 return( $self->_compile_chained_operation( $expression ) )
2580             if $expression =~ /^$chained_operation_regexp$/so;
2581              
2582             # "cannot happen".
2583 0         0 $self->error( "Unrecognised atomic expression element: $expression" );
2584             }
2585              
2586             # TODO: replace with "replace tightest-binding operator with subtree"
2587             # while-loop, rather than recursive divide-and-conquer by
2588             # loosest-binding operator.
2589             # will eleminate the depth*depth cartesian loops.
2590             sub _build_op_tree
2591             {
2592 801     801   1174 my ( $self, $arr ) = @_;
2593 801         797 my ( $highest_weight, $highest_pos, $op, $lhs, $rhs );
2594              
2595             #print "build_op_tree( ", Data::Dumper::Dumper( $arr ), "\n";
2596              
2597             # "cannot happen"
2598 801 50       796 $self->error( "Empty expression" ) unless @{$arr};
  801         1806  
2599              
2600             # TODO: cache @{$arr} size.
2601 801         1216 for( my $i = 0; $i < @{$arr}; $i += 2 )
  1923         4391  
2602             {
2603             # TODO: this is a crappy hack to provide compat with recursion.
2604 1122 100       2703 next if ref( $arr->[ $i ] );
2605 639         1333 $arr->[ $i ] = $self->_compile_expression( $arr->[ $i ] );
2606             }
2607              
2608 801 100       854 return( $arr->[ 0 ] ) if @{$arr} == 1;
  801         2277  
2609              
2610             # Look for literals to fold together.
2611             #print "Looking at: ", Data::Dumper::Dumper( $arr ), "\n";
2612 320         514 for( my $i = 1; $i < @{$arr} - 1; $i += 2 )
  641         1591  
2613             {
2614 321         331 my ( $op, $weight );
2615              
2616 321         454 $op = $arr->[ $i ];
2617 321         600 $weight = $operators{ $op }->[ 0 ];
2618              
2619 321         448 $lhs = $arr->[ $i - 1 ];
2620 321         438 $rhs = $arr->[ $i + 1 ];
2621              
2622             #print " Looking at op $i: '$op'\n";
2623             # If we're higher or equal precedence to the operators either
2624             # side of us, and our lhs and rhs are literal values, we're
2625             # eligible for folding.
2626 321 100 66     1113 if( ( ( $i < 3 ) or
      66        
      33        
      100        
      100        
2627             ( $weight <= $operators{ $arr->[ $i - 2 ] }->[ 0 ] ) ) and
2628             ( ( $i >= @{$arr} - 2 ) or
2629             ( $weight <= $operators{ $arr->[ $i + 2 ] }->[ 0 ] ) ) and
2630             ( $lhs->[ 0 ] == LITERAL ) and ( $rhs->[ 0 ] == LITERAL ) )
2631             {
2632 79         91 my ( $original );
2633              
2634             # Rebuild of "original" is surely hackery of the finest order. :(
2635 79 50       377 $original = ( $lhs->[ 3 ] ? "( $lhs->[ 1 ] )" : $lhs->[ 1 ] ) .
    50          
2636             " $op " .
2637             ( $rhs->[ 3 ] ? "( $rhs->[ 1 ] )" : $rhs->[ 1 ] );
2638              
2639 79         107 splice( @{$arr}, $i - 1, 3,
  79         327  
2640             [ LITERAL, $original,
2641             $self->_eval_op( $op, $lhs, $rhs ), 1 ] );
2642 79 50       328 $i = ( $i <= 3 ) ? 1 : $i - 4;
2643             #print " Folding, arr becomes: ", Data::Dumper::Dumper( $arr ), ", i = $i\n";
2644             }
2645             }
2646              
2647 320 100       380 return( $arr->[ 0 ] ) if @{$arr} == 1;
  320         965  
2648              
2649 241         310 $highest_weight = 0;
2650 241         336 for( my $i = 1; $i < @{$arr} - 1; $i += 2 )
  483         1280  
2651             {
2652 242         247 my ( $op );
2653              
2654 242         348 $op = $arr->[ $i ];
2655             #print "looking at op $i: $op\n";
2656 242 100       592 if( $operators{ $op }->[ 0 ] > $highest_weight )
2657             {
2658 241         410 $highest_weight = $operators{ $op }->[ 0 ];
2659 241         433 $highest_pos = $i;
2660             }
2661             }
2662             #print "highest_pos = $highest_pos, highest_op = $highest_op\n";
2663              
2664 241         348 $op = $arr->[ $highest_pos ];
2665 241         466 $lhs = $self->_build_op_tree( [ @{$arr}[ 0..$highest_pos - 1 ] ] );
  241         735  
2666 241         558 $rhs = $self->_build_op_tree( [ @{$arr}[ $highest_pos + 1..@{$arr} - 1 ] ] );
  241         623  
  241         349  
2667              
2668 241         1273 return( [ OP_TREE, '', $op, $lhs, $rhs ] );
2669             }
2670              
2671             sub _build_var
2672             {
2673 2095     2095   3708 my ( $self, $segments, $originals, $original ) = @_;
2674 2095         2646 my @segments = @{$segments};
  2095         5388  
2675 2095         2870 my @originals = @{$originals};
  2095         4736  
2676              
2677             # If we're just a subexpression with no subscripts, just return
2678             # the subexpression.
2679 2095 100 100     9771 return( $segments[ 0 ] )
2680             if @segments == 1 and ref( $segments[ 0 ] );
2681              
2682 1818 100       4891 if( $segments[ @segments - 1 ] eq '__size__' )
2683             {
2684 4         5 pop @segments;
2685 4         7 pop @originals;
2686 4         17 return( [ FUNC, $original, 'size',
2687             [ $self->_build_var( \@segments, \@originals, $original ) ],
2688             ] );
2689             }
2690              
2691 1814         13023 return( [ VAR, $original, \@segments, \@originals, @segments - 1 ] );
2692             }
2693              
2694             sub _compile_chained_operation
2695             {
2696 1816     1816   3212 my ( $self, $chain ) = @_;
2697 1816         2064 my ( $original, $original_so_far, @segments, @originals, $segment,
2698             $subscript );
2699              
2700             #print "compile_chained_operation( $chain )\n";
2701              
2702 1816 100       4745 return( $symbolic_literals{ $chain } )
2703             if exists( $symbolic_literals{ $chain } );
2704              
2705              
2706 1813         2533 $original = $chain;
2707              
2708 1813         3345 @segments = @originals = ();
2709 1813 50       19434 if( ( $segment, $chain ) =
2710             $chain =~ $capture_chained_operation_top_regexp )
2711             {
2712             # $segment = $1;
2713             # $chain = $2 || '';
2714 1813         2576 $original_so_far = $segment;
2715              
2716             #print "Capture top on '$original', segment '$segment', chain '$chain'\n";
2717              
2718 1813 100       7572 if( $segment =~ $capture_function_regexp )
2719             {
2720 323         594 push @originals, $segment;
2721 323         936 $segment = $self->_compile_function( $1, $2, $original );
2722 282 100       1002 return( $segment ) unless $chain;
2723 162 50       346 $segment = $segment->[ 2 ] if $segment->[ 0 ] == LITERAL;
2724 162         344 push @segments, $segment;
2725             }
2726             else
2727             {
2728 1490         3113 push @segments, $segment;
2729 1490         2971 push @originals, $segment;
2730             }
2731             }
2732             else
2733             {
2734             # TODO FIXME: error.
2735 0         0 die "FIXME";
2736             }
2737              
2738 1652   66     8526 while( $chain and
2739             ( $segment, $chain ) =
2740             $chain =~ $capture_chained_operation_subscript_regexp )
2741             {
2742             # $segment = $1;
2743             # $chain = $2 || '';
2744             #print "Segment: $segment\nRest: $chain\n";
2745              
2746             # TODO: use a capture rather than m// and s///
2747 1390 100       8710 if( $segment =~ $capture_literal_subscript_regexp )
2748             {
2749             #print " Literal\n";
2750 486         891 $original_so_far .= $segment;
2751 486         1230 push @segments, $1;
2752 486         863 push @originals, $1;
2753 486         3366 next;
2754             }
2755 904 100       5690 if( ( $subscript ) = $segment =~ $capture_expr_subscript_regexp )
2756             {
2757             #print " Expr\n";
2758             # var[ ... ] expression subscript notation.
2759              
2760 465         681 $original_so_far .= $segment;
2761             # $subscript = $1;
2762 465         1129 my $index = $self->_compile_expression( $subscript );
2763              
2764             # If it's a constant push it up as if it
2765             # was a dotted literal index.
2766 465 100       1173 if( $index->[ 0 ] == LITERAL )
2767             {
2768 451         1022 push @segments, $index->[ 2 ];
2769             }
2770             else
2771             {
2772 14         25 push @segments, $index;
2773             }
2774             # $subscript =~ s/^\s+//o;
2775             # $subscript =~ s/\s+$//o;
2776 465         699 push @originals, $subscript;
2777 465         3828 next;
2778             }
2779             # TODO: use a capture rather than m// and s///
2780 439 50       3597 if( my ( $method, $args ) =
2781             $segment =~ $capture_method_subscript_regexp )
2782             {
2783             #print " Method\n";
2784              
2785 439         1456 my $var = $self->_build_var( \@segments, \@originals,
2786             $original_so_far );
2787              
2788 439 100       1548 if( $self->{ vmethods } )
2789             {
2790             # We convert "vmethods" into our normal function style.
2791              
2792 1         6 my $func = $self->_compile_function(
2793             $method, $args, $original, [ $var ] );
2794              
2795             # Fold if it's a literal.
2796 1 50       13 $func = $func->[ 2 ] if $func->[ 0 ] == LITERAL;
2797 1         3 @segments = ( $func );
2798             }
2799             else
2800             {
2801 438         945 $args = $self->_compile_function_args( $args );
2802 438         1974 @segments = ( [ METHOD, $original, $var, $method, $args ] );
2803             }
2804 439         937 @originals = ( $original_so_far );
2805 439         650 $original_so_far .= $segment;
2806 439         4494 next;
2807             }
2808             # TODO FIXME: error.
2809 0         0 die "FIXME";
2810             }
2811              
2812 1652 50       3410 $self->error( "Malformed variable segment: '$chain' in '$original'" )
2813             if $chain;
2814              
2815 1652         4534 return( $self->_build_var( \@segments, \@originals, $original ) );
2816             }
2817              
2818             sub _compile_function
2819             {
2820 324     324   1047 my ( $self, $func, $args, $expression, $prepend_args ) = @_;
2821 324         433 my ( $numargs, $func_def );
2822              
2823             # $args = length( $args) > 2 ? substr( $args, 1, -2 ) : '';
2824              
2825 324 100       1227 $func_def = $functions{ $func } if $functions{ $func };
2826             $func_def = $self->{ local_functions }->{ $func }
2827             if $self->{ local_functions } and
2828 324 100 100     1820 $self->{ local_functions }->{ $func };
2829              
2830 324 100       981 $self->error( "Unknown function: $func" ) unless $func_def;
2831              
2832 285         729 $args = $self->_compile_function_args( $args );
2833 285 100       713 unshift @{$args}, @{$prepend_args} if $prepend_args;
  1         2  
  1         3  
2834              
2835             # Check the number of args.
2836 285 100       773 if( ( $numargs = $func_def->[ FUNC_ARG_NUM ] ) >= 0 )
2837             {
2838 1         5 $self->error( "too few args to $func(), expected $numargs " .
2839 284         777 "and got " . @{$args} . " in $expression" )
2840 284 100       322 if @{$args} < $numargs;
2841 1         6 $self->error( "too many args to $func(), expected $numargs " .
2842 283         774 "and got " . @{$args} . " in $expression" )
2843 283 100       562 if @{$args} > $numargs;
2844             }
2845              
2846 283 100       754 unless( $func_def->[ FUNC_INCONST ] )
2847             {
2848 282         323 my ( $nonliteral );
2849              
2850 282         344 foreach my $arg ( @{$args} )
  282         657  
2851             {
2852 245 100       681 next if $arg->[ 0 ] == LITERAL;
2853 178         206 $nonliteral = 1;
2854 178         321 last;
2855             }
2856              
2857             #CORE::warn( "$expression has " . ( $nonliteral ? "nonliteral" : "literal" ) . " args" );
2858 282 100       707 unless( $nonliteral )
2859             {
2860 104         109 my ( $ret );
2861              
2862 104         318 $ret = $self->_eval_function( $func, $args );
2863              
2864 1         4 return( [ LITERAL, $expression,
2865 104 100       967 ( ( ref( $ret ) eq 'SCALAR' ) ? ${$ret} : $ret ), 1 ] );
2866             }
2867             }
2868              
2869 179 100       433 unshift @{$args}, [ TEMPLATE ]
  1         3  
2870             if $func_def->[ FUNC_NEEDS_TEMPLATE ];
2871              
2872 179         661 return( [ FUNC, $expression, $func, $args ] );
2873             }
2874              
2875             sub _compile_function_args
2876             {
2877 723     723   1198 my ( $self, $arglist ) = @_;
2878 723         973 my ( $original, @args, $nextarg );
2879              
2880 723         1558 $arglist =~ s/^\s+//;
2881 723         1224 $arglist =~ s/\s+$//;
2882              
2883 723         968 $original = $arglist;
2884              
2885 723         1021 @args = ();
2886 723   100     10954 while( defined( $arglist ) and length( $arglist ) and
      66        
2887             ( $nextarg, $arglist ) =
2888             ( $arglist =~ $capture_expr_comma_remain_regexp ) )
2889             {
2890             # $nextarg = $1;
2891             # $arglist = $2;
2892 687         1808 push @args, $self->_compile_expression( $nextarg );
2893             }
2894             $self->error(
2895 723 50       1636 "Malformed function arguments list: '$arglist' in '$original'" )
2896             if $arglist;
2897 723         1969 return( \@args );
2898             }
2899              
2900             sub _eval_expression
2901             {
2902 3408     3408   4889 my ( $self, $expr, $undef_ok ) = @_;
2903 3408         3858 my ( $type, $val );
2904              
2905             # "can't happen" in normal use, will error on next line anyway.
2906             # $self->error( "Bad arg to _eval_expression(): $expr" )
2907             # unless ref( $expr );
2908              
2909             #$self->{ exprcount }->{ $type }++;
2910             #my $exprstart = Time::HiRes::time();
2911 3408 100       11044 if( ( $type = $expr->[ 0 ] ) == LITERAL )
    100          
    100          
    100          
    100          
    100          
    50          
2912             {
2913 958         1565 $val = $expr->[ 2 ];
2914             }
2915             elsif( $type == VAR )
2916             {
2917 1499         1974 $val = $self->_eval_var( @{$expr}, $undef_ok );
  1499         4397  
2918             }
2919             elsif( $type == OP_TREE )
2920             {
2921             # $val = $self->_eval_op( $expr->[ 2 ], $expr->[ 3 ], $expr->[ 4 ] );
2922             # WARNING: this is unrolled below from _eval_op: keep in sync.
2923             #eval
2924             #{
2925 245         418 $val = $operators{ $expr->[ 2 ] };
2926             # Do we defer evaluation or not?
2927 245 100       489 if( $val->[ 2 ] )
2928             {
2929 49         122 $val = $val->[ 1 ]->( $self, $expr->[ 3 ], $expr->[ 4 ] );
2930             }
2931             else
2932             {
2933 196         495 $val = $val->[ 1 ]->( $self,
2934             $self->_eval_expression( $expr->[ 3 ] ),
2935             $self->_eval_expression( $expr->[ 4 ] ) );
2936             }
2937             #};
2938             #$self->error( "$@" ) if $@;
2939             }
2940             elsif( $type == UNARY_OP )
2941             {
2942             # TODO: unroll? common enough to bother?
2943 57         153 $val = $self->_eval_unary_op( $expr->[ 2 ], $expr->[ 3 ] );
2944             }
2945             elsif( $type == FUNC )
2946             {
2947             # $val = $self->_eval_function( $expr->[ 2 ], $expr->[ 3 ] );
2948             # WARNING: this is unrolled below from _eval_function: keep in sync.
2949              
2950             #warn "Eval func $expr->[ 2 ] against " . _tinydump( [ @function_table ] );
2951             # $val = $function_table[ $expr->[ 2 ] ];
2952              
2953             # TODO: should copy_global_functions block class-function lookup?
2954 206 100       838 $val = $functions{ $expr->[ 2 ] } if $functions{ $expr->[ 2 ] };
2955             $val = $self->{ local_functions }->{ $expr->[ 2 ] }
2956             if $self->{ local_functions } and
2957 206 100 100     1394 $self->{ local_functions }->{ $expr->[ 2 ] };
2958 206 100       580 $self->error( "Unknown function: $expr->[ 2 ]" ) unless $val;
2959 204 100       435 if( $val->[ FUNC_UNDEF_OK ] )
2960             {
2961 26         58 $val = $val->[ FUNC_FUNC ]->(
2962 26         32 map { $self->_eval_expression( $_, 1 ) } @{$expr->[ 3 ]} );
  26         62  
2963             }
2964             else
2965             {
2966 180         430 $val = $val->[ FUNC_FUNC ]->(
2967 178         283 map { $self->_eval_expression( $_ ) } @{$expr->[ 3 ]} );
  178         436  
2968             }
2969             }
2970             elsif( $type == METHOD )
2971             {
2972 442         1368 $val = $self->_eval_method( $expr->[ 2 ], $expr->[ 3 ], $expr->[ 4 ] );
2973             }
2974             elsif( $type == TEMPLATE )
2975             {
2976 1         4 return( $self );
2977             }
2978             else
2979             {
2980 0         0 $self->error( "Unknown expression opcode: $type" );
2981             }
2982             #$self->{ exprprofile }->{ $type } += Time::HiRes::time() - $exprstart;
2983              
2984             # Undef warning.
2985 3394 100 100     9348 $self->warning( "undefined template value '$expr->[ 1 ]'" )
      66        
2986             unless defined( $val ) or $undef_ok or $expr->[ 1 ] eq 'undef';
2987              
2988 3394         9948 return( $val );
2989             }
2990              
2991             sub _eval_op
2992             {
2993 79     79   161 my ( $self, $op, $lhs, $rhs ) = @_;
2994              
2995             #my $ret;
2996             #$self->{ opcount }->{ $op }++;
2997             #my $opstart = Time::HiRes::time();
2998             #$ret = $operators{ $op }->[ 1 ]->( $self, $lhs, $rhs );
2999             #$self->{ opprofile }->{ $op } += Time::HiRes::time() - $opstart;
3000             #return( $ret );
3001              
3002             # WARNING: this function is unrolled above in _eval_expr: keep in sync.
3003              
3004 79         205 $op = $operators{ $op };
3005              
3006             # Do we defer evaluation or not?
3007 79 100       308 return( $op->[ 1 ]->( $self,
3008             $self->_eval_expression( $lhs ),
3009             $self->_eval_expression( $rhs ) ) )
3010             unless $op->[ 2 ];
3011              
3012 16         47 return( $op->[ 1 ]->( $self, $lhs, $rhs ) );
3013             }
3014              
3015             sub _eval_unary_op
3016             {
3017 81     81   123 my ( $self, $op, $expr ) = @_;
3018              
3019             # "|| 0" is there because !1 in perl is '' but we want 0.
3020             # !'' gives 1, so seems reasonable !'whatever' should be 0 too not ''.
3021 81 100 100     295 return( !$self->_eval_expression( $expr, 1 ) || 0 )
3022             if $op eq '!';
3023 65 100 100     201 return( ( not $self->_eval_expression( $expr, 1 ) ) || 0 )
3024             if $op eq 'not';
3025             # TODO: This is odd for strings, probably should error or warn.
3026 17 50       69 return( -$self->_eval_expression( $expr ) )
3027             if $op eq '-';
3028              
3029 0         0 $self->error( "Unknown unary operator: '$op'" );
3030             }
3031              
3032             sub _assign_var
3033             {
3034 1     1   2 my ( $self, $lhs, $rhs ) = @_;
3035 1         2 my ( $var_stack, $counter, $sz, $var );
3036              
3037             # TODO: this should be compile-time ideally.
3038 1 50       4 $self->error( "Invalid LHS to assignment: $lhs->[ 1 ]" )
3039             if $lhs->[ 0 ] != VAR;
3040              
3041             # TODO: this should be compile-time ideally.
3042 1         4 $self->error( "Can only assign to top-level variables: $lhs->[ 1 ]" )
3043 1 50       2 if @{$lhs->[ 2 ]} > 1;
3044              
3045 1         3 $var = $lhs->[ 2 ]->[ 0 ];
3046              
3047 1         2 $var_stack = $self->{ var_stack };
3048 1         3 $var_stack->[ 0 ]->{ $var } = $rhs;
3049 1         2 $sz = @{$var_stack};
  1         2  
3050 1         2 $counter = 1;
3051 1         4 while( $counter < $sz )
3052             {
3053 0 0       0 return( $rhs ) unless exists( $var_stack->[ $counter ]->{ $var } );
3054 0         0 $var_stack->[ $counter ]->{ $var } = $rhs;
3055 0         0 $counter++;
3056             }
3057              
3058 1         4 return( $rhs );
3059             }
3060              
3061             sub _eval_var
3062             {
3063             # The stem value _is_ the value if there's no other segments.
3064             # This is pulled above the sub's argument extraction for speed, I
3065             # will rot in hell for this, but it _is_ performance-critical.
3066 1499 100   1499   5167 return( $_[ 0 ]->{ var_stack_top }->{ $_[ 3 ]->[ 0 ] } )
3067             unless $_[ 5 ];
3068              
3069 644         1321 my ( $self, $instr, $original, $segments, $originals, $last, $undef_ok ) = @_;
3070 644         793 my ( $val, $stem, $i, $special_values, $leaf, $type );
3071              
3072 644         1001 $stem = $segments->[ 0 ];
3073 644         1161 $special_values = $self->{ special_values };
3074              
3075             # Check to see if it's a special loop variable or something.
3076 644 100 66     4184 if( $last >= 1 and
      100        
3077             $special_values->{ $stem } and
3078             exists( $special_values_names{ $segments->[ 1 ] } ) )
3079             {
3080             # Don't bother checking that the leaf isn't a ref, it won't
3081             # match a key and saves on a ref() call when it isn't.
3082 160         310 $val = $special_values->{ $stem }->[
3083             $special_values_names{ $segments->[ 1 ] } ];
3084 160         268 $i = 2;
3085             }
3086             else
3087             {
3088 484         605 $i = 1;
3089             # Determine the stem (top-level) value
3090 484 100       1117 if( ref( $stem ) )
3091             {
3092             # Top level is an expression not a var.
3093 324         851 $val = $self->_eval_expression( $stem );
3094             }
3095             else
3096             {
3097 160         503 $val = $self->{ var_stack_top }->{ $stem };
3098             }
3099             }
3100              
3101             # Navigate our way down the remaining segments.
3102 644         1892 for( ; $i <= $last; $i++ )
3103             {
3104 926 100       2332 if( ref( $leaf = $segments->[ $i ] ) )
3105             {
3106             # It's an index expression of the style var[index]
3107 22 100       48 unless( defined( $leaf = $self->_eval_expression( $leaf ) ) )
3108             {
3109 2 50       7 return( undef ) if $undef_ok;
3110 2         13 $self->error(
3111             "Undefined index '$originals->[ $i ]' in " .
3112             "'$original'" );
3113             }
3114              
3115             # Check to see if it's a special loop variable or something.
3116             # Only need to do this if we're an EXPR subscript, constant
3117             # ones will have been checked outside the loop.
3118 20 100       51 if( $i == 1 )
3119             {
3120 15 100 66     60 if( $special_values->{ $stem } and
3121             exists( $special_values_names{ $leaf } ) )
3122             {
3123 5         9 $val = $special_values->{ $stem }->[
3124             $special_values_names{ $leaf } ];
3125 5         14 next;
3126             }
3127             }
3128             }
3129              
3130 919 100       1792 unless( defined( $val ) )
3131             {
3132 2 50       7 return( undef ) if $undef_ok;
3133 2 50       21 $self->error(
3134             "Can't get key '$leaf' " .
3135             ( $originals->[ $i ] ne $leaf ?
3136             "(from '$originals->[ $i ]') " : "" ) .
3137             #"(with segments " . Data::Dumper::Dumper( $segments ) . ") " .
3138             "of undefined parent in '$original'" );
3139             }
3140              
3141 917 100       3079 if( not ( $type = ref( $val ) ) )
    100          
3142             {
3143             #use Data::Dumper;
3144             #warn "originals = " . Data::Dumper::Dumper( $originals ) . "\ni = $i\nleaf = $leaf\noriginal = $original\nsegments = " . Data::Dumper::Dumper( $segments ) . "\n";
3145              
3146 2 50       29 $self->error(
3147             "Can't get key '$leaf' " .
3148             ( $originals->[ $i ] ne $leaf ?
3149             "(from '$originals->[ $i ]') " : "" ) .
3150             "of non-reference parent in '$original'" );
3151             }
3152             elsif( $type eq 'ARRAY' )
3153             {
3154 9 50       66 $self->error(
    100          
3155             "Can't index array-reference with string '$leaf' " .
3156             ( $originals->[ $i ] ne $leaf ?
3157             "(from '$originals->[ $i ]') " : "" ) .
3158             "in '$original'" )
3159             unless $leaf =~ /^\d+$/o;
3160 7 50       42 $val = defined( $val->[ $leaf ] ) ? $val->[ $leaf ] : undef;
3161             }
3162             else
3163             {
3164 906 100       3772 $val = defined( $val->{ $leaf } ) ? $val->{ $leaf } : undef;
3165             }
3166             }
3167              
3168 636         1974 return( $val );
3169             }
3170              
3171             sub _eval_function
3172             {
3173 104     104   192 my ( $self, $func, $args ) = @_;
3174 104         116 my ( $val );
3175              
3176             # WARNING: this function is unrolled above in _eval_expr: keep in sync.
3177              
3178             # TODO: should copy_global_functions block class-function lookup?
3179 104 100       282 $val = $functions{ $func } if $functions{ $func };
3180             $val = $self->{ local_functions }->{ $func }
3181             if $self->{ local_functions } and
3182 104 100 66     435 $self->{ local_functions }->{ $func };
3183 104 50       244 $self->error( "Unknown function: $func" ) unless $val;
3184              
3185 104 100       223 if( $val->[ FUNC_UNDEF_OK ] )
3186             {
3187 2         3 $args = [ map { $self->_eval_expression( $_, 1 ) } @{$args} ];
  2         6  
  2         4  
3188             }
3189             else
3190             {
3191 102         139 $args = [ map { $self->_eval_expression( $_ ) } @{$args} ];
  65         165  
  102         228  
3192             }
3193              
3194             #$self->{ funccount }->{ $func }++;
3195             #my $ret;
3196             #my $start_time = Time::HiRes::time();
3197             # $ret = $functions{ $func }->[ 1 ]->( $self, @{$args} );
3198             #$self->{ funcprofile }->{ $func } += Time::HiRes::time() - $start_time;
3199             # return( $ret );
3200              
3201 104 100       266 if( $val->[ FUNC_NEEDS_TEMPLATE ] )
3202             {
3203 1         3 return( $val->[ FUNC_FUNC ]->( $self, @{$args} ) );
  1         5  
3204             }
3205             else
3206             {
3207 103         119 return( $val->[ FUNC_FUNC ]->( @{$args} ) );
  103         410  
3208             }
3209             }
3210              
3211             sub _eval_method
3212             {
3213 442     442   904 my ( $self, $expr, $method, $args ) = @_;
3214 442         598 my ( $exprdesc, $ret );
3215              
3216 442         810 $exprdesc = $expr->[ 1 ];
3217 442         1063 $expr = $self->_eval_expression( $expr );
3218              
3219 442 100       996 $self->error( "Can't call method on undefined value $exprdesc" )
3220             unless defined $expr;
3221 441 100       1292 $self->error( "Can't call method on non-reference value $exprdesc: $expr" )
3222             unless ref( $expr );
3223              
3224             # For security reasons we don't want to allow calling
3225             # just any old method on any old object from within a
3226             # potentially user-defined template.
3227 440 100       1341 $self->error( 'Invalid method to call from within a template: ' .
3228             ref( $expr ) . "->$method" )
3229             unless $expr->valid_template_method( $method );
3230              
3231 439         2366 $args = [ map { $self->_eval_expression( $_ ) } @{$args} ];
  440         902  
  439         860  
3232              
3233 439         732 $ret = $expr->$method( @{$args} );
  439         1569  
3234              
3235 439         3100 return( $ret );
3236             }
3237              
3238             sub run
3239             {
3240 1117     1117 1 77425 my $self = $_[ 0 ];
3241             # $line, $instr, $value lexically belong inside the loop,
3242             # but in such a tight loop it's a performance hit, they're
3243             # initialized at the start of each use anyway.
3244             # If oddness ensues, move this line into the head of the loop and
3245             # see if oddness abates.
3246 1117         1565 my ( $lineno, $ret, @var_stack, @for_stack, $run_start,
3247             $program, $last_instr, $special_values, $line, $instr, $value );
3248              
3249             # For large templates this tricks perl's memory handling into
3250             # giving us a big chunk of contiguous memory so that $ret .= $whatever
3251             # doesn't have to keep incrementally adding more memory, this can
3252             # give a minor-but-not-insignificant speed boost of ~2-3%.
3253             # This may be highly sensitive to perl version and OS, and I'm
3254             # not sure it does nice things to the memory profile of the process
3255             # either...
3256 1117         47664 $ret = ' ' x 80_000;
3257 1117         1820 $ret = '';
3258 1117         1512 $lineno = 0;
3259              
3260 1117         2640 @var_stack = ( $self->{ vars } );
3261 1117         1538 @for_stack = ();
3262              
3263 1117         2504 $self->{ var_stack } = \@var_stack;
3264 1117         2110 $self->{ var_stack_top } = $var_stack[ 0 ];
3265              
3266 1117         2118 $self->{ phase } = 'runtime';
3267              
3268             #my $total_instr = 0;
3269              
3270             #foreach my $prof ( qw/instr expr func op/ )
3271             #{
3272             # $self->{ "${prof}count" } = {};
3273             # $self->{ "${prof}profile" } = {};
3274             #}
3275              
3276             # Local unroll of some of our properties
3277 1117         1975 $program = $self->{ template }->{ program };
3278 1117         1743 $last_instr = $self->{ template }->{ last_instr };
3279             # @function_table =
3280             # map { $functions{ $_ } } @{$self->{ template }->{ function_table }};
3281 1117         1785 $special_values = $self->{ special_values };
3282              
3283 1117         3141 while( $lineno <= $last_instr )
3284             {
3285 2103         3398 $line = $program->[ $lineno++ ];
3286 2103         3678 $self->{ current_pos } = $line->[ 1 ];
3287              
3288             # TODO: look at $pos->[ 0 ] to determine file and recreate
3289             # the "stack" for error traces if neccessary.
3290              
3291             #$self->{ instrcount }->{ $line->[ 0 ] }++;
3292             #my $instrstart = Time::HiRes::time();
3293 2103 100       6714 if( ( $instr = $line->[ 0 ] ) == LITERAL )
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
    0          
3294             {
3295 654         2023 $ret .= $line->[ 2 ];
3296             }
3297             elsif( $instr == EXPR )
3298             {
3299 952         2577 $value = $self->_eval_expression( $line->[ 2 ] );
3300 939 100       5217 $ret .= ( ( ref( $value ) eq 'SCALAR' ) ? ${$value} : $value )
  1 100       4  
3301             unless $line->[ 3 ];
3302             }
3303             elsif( $instr == JUMP )
3304             {
3305             #$ret .= "[jump]";
3306 110         295 $lineno = $line->[ 2 ];
3307             }
3308             elsif( $instr == JUMP_IF )
3309             {
3310             #$ret .= "[jump if/unless $line->[3]]";
3311 102         264 $value = $self->_eval_expression( $line->[ 3 ], 1 );
3312             # $value = not $value if $line->[ 4 ];
3313 102 100       3095 $lineno = $line->[ 2 ] unless $value;
3314             }
3315             elsif( $instr == FOR )
3316             {
3317 55         65 my ( $iterator, $set, $set_value, $hash, $last, $specials_needed );
3318              
3319 55         75 $iterator = $line->[ 3 ];
3320 55         97 $set = $line->[ 4 ];
3321 55         90 $specials_needed = $line->[ 5 ];
3322              
3323 55         231 $set_value = $self->_eval_expression( $set, 1 );
3324 55 100       127 $set_value = [] unless defined $set_value;
3325              
3326 55 100       213 if( ref( $set_value ) eq 'HASH' )
    100          
3327             {
3328 16         20 $hash = $set_value;
3329 16         19 $set_value = [ sort( keys( %{$set_value} ) ) ];
  16         84  
3330             }
3331             elsif( not ref( $set_value ) )
3332             {
3333             # If it's a number make it into a loop of 0..number.
3334             # If they want 1..number they can <: if x != 1 :> inside it.
3335 30         115 $set_value = [ 0..int( $set_value ) ];
3336             }
3337              
3338             # TODO: assign and compare
3339 55         74 $last = @{$set_value} - 1;
  55         100  
3340 55 100       115 if( $last == -1 )
3341             {
3342 3         13 $lineno = $line->[ 2 ];
3343             }
3344             else
3345             {
3346 52         58 my ( $context );
3347              
3348 52         74 $value = $set_value->[ 0 ];
3349 52 100       273 $special_values->{ $iterator } =
    100          
    100          
    100          
3350             [
3351             0,
3352             1,
3353             0,
3354             1,
3355             0,
3356             $last == 0 ? 1 : 0,
3357             undef,
3358             $last == 0 ?
3359             undef : $set_value->[ 1 ],
3360             $hash ? $hash->{ $value } : undef,
3361             ]
3362             if $specials_needed;
3363             # Optimization: only create a new context if needed.
3364 52 100       151 if( $var_stack[ 0 ]->{ $iterator } )
3365             {
3366 3         4 $context = { %{$var_stack[ 0 ]} };
  3         9  
3367 3         6 $context->{ $iterator } = $value;
3368 3         4 unshift @var_stack, $context;
3369 3         5 $self->{ var_stack_top } = $context;
3370             }
3371             else
3372             {
3373 49         113 $var_stack[ 0 ]->{ $iterator } = $value;
3374             }
3375 52 100       281 unshift @for_stack, [
3376             0, $last, $set_value, $hash, $context ? 1 : 0,
3377             $specials_needed,
3378             ];
3379             }
3380             }
3381             elsif( $instr == END_FOR )
3382             {
3383 205         228 my ( $iterator, $set_value, $counter, $hash, $last,
3384             $specials_needed );
3385              
3386 205         249 $iterator = $line->[ 3 ];
3387              
3388 205         300 $counter = ++$for_stack[ 0 ]->[ LOOP_STACK_COUNTER ];
3389 205         250 $last = $for_stack[ 0 ]->[ LOOP_STACK_LAST ];
3390              
3391 205 100       346 if( $counter <= $last )
3392             {
3393 153         232 $set_value = $for_stack[ 0 ]->[ LOOP_STACK_SET ];
3394 153         195 $hash = $for_stack[ 0 ]->[ LOOP_STACK_HASH ];
3395 153         174 $specials_needed = $for_stack[ 0 ]->[ LOOP_STACK_SPECIALS ];
3396              
3397 153         254 $var_stack[ 0 ]->{ $iterator } = $set_value->[ $counter ];
3398 153 100       895 $special_values->{ $iterator } =
    100          
    100          
    100          
    100          
    100          
3399             [
3400             $counter,
3401             ( $counter % 2 ) ? 0 : 1,
3402             $counter % 2,
3403             0,
3404             $counter == $last ? 0 : 1,
3405             $counter == $last ? 1 : 0,
3406             $set_value->[ $counter - 1 ],
3407             $counter == $last ?
3408             undef :
3409             $set_value->[ $counter + 1 ],
3410             $hash ? $hash->{ $set_value->[ $counter ] } : undef,
3411             ]
3412             if $specials_needed;
3413              
3414 153         639 $lineno = $line->[ 2 ];
3415             }
3416             else
3417             {
3418 52 100       111 if( $for_stack[ 0 ]->[ LOOP_STACK_CONTEXT ] )
3419             {
3420 3         6 shift @var_stack;
3421 3         8 $self->{ var_stack_top } = $var_stack[ 0 ];
3422             }
3423             else
3424             {
3425 49         100 delete $var_stack[ 0 ]->{ $iterator };
3426             }
3427 52         66 shift @for_stack;
3428 52         236 delete $special_values->{ $iterator };
3429             }
3430             }
3431             elsif( $instr == CONTEXT_PUSH )
3432             {
3433 6         7 my ( $context, $new_context );
3434              
3435             # TODO: needed ||? empty contexts should be optimized away now.
3436 6   50     15 $new_context = $line->[ 2 ] || {};
3437 6         7 $context = { %{$var_stack[ 0 ]} };
  6         30  
3438 6         11 foreach my $var ( keys( %{$new_context} ) )
  6         13  
3439             {
3440 8         18 $context->{ $var } = $self->_eval_expression(
3441             $new_context->{ $var }, 1 )
3442             }
3443 6         12 unshift @var_stack, $context;
3444 6         19 $self->{ var_stack_top } = $context;
3445             }
3446             elsif( $instr == CONTEXT_POP )
3447             {
3448             #$ret .= "[context_pop]";
3449 6         7 shift @var_stack;
3450 6         22 $self->{ var_stack_top } = $var_stack[ 0 ];
3451             }
3452             # TODO: ick, hate cut-n-paste code.
3453             # TODO: unroll constant parts of hash lookups to local var
3454             elsif( $self->{ local_syntaxes }->{ '.instr' }->{ $instr } )
3455             {
3456 11         15 my ( $executor, $token );
3457              
3458 11         35 $token = $self->{ local_syntaxes }->{ '.instr' }->{ $instr };
3459 11         27 $executor = $self->{ local_syntaxes }->{ $token }->{ run };
3460 11         43 $value = $executor->( $self, $token, $line->[ 2 ] );
3461 11 100       144 $ret .= $value if defined $value;
3462             }
3463             # TODO: ick, hate cut-n-paste code.
3464             # TODO: unroll constant parts of hash lookups to local var
3465             elsif( $syntaxes{ '.instr' }->{ $instr } )
3466             {
3467 2         7 my ( $executor, $token );
3468              
3469 2         1124 $token = $syntaxes{ '.instr' }->{ $instr };
3470 2         9 $executor = $syntaxes{ $token }->{ run };
3471 2         12 $value = $executor->( $self, $token, $line->[ 2 ] );
3472 2 50       26 $ret .= $value if defined $value;
3473             }
3474             elsif( $instr == DEBUG )
3475             {
3476             $self->{ debug }->{ $line->[ 2 ]->{ type } } =
3477 0         0 ( $line->[ 2 ]->{ state } eq 'on' );
3478             }
3479             #$self->{ instrprofile }->{ $instr } += Time::HiRes::time() - $instrstart;
3480             }
3481              
3482 1104         2212 delete $self->{ current_pos };
3483 1104         1696 delete $self->{ var_stack };
3484 1104         2021 delete $self->{ var_stack_top };
3485 1104         1912 delete $self->{ phase };
3486              
3487 1104         12615 return( \$ret );
3488             }
3489              
3490             sub _tersedump
3491             {
3492 1     1   789 return( Data::Dumper->new( [ @_ ] )->Terse(1)->Useqq(1)->Dump() );
3493             }
3494              
3495             sub _tinydump
3496             {
3497 28     28   1152 return( Data::Dumper->new( [ @_ ] )->Indent(0)->Quotekeys(0)->Pair('=>')->Terse(1)->Useqq(1)->Dump() );
3498             }
3499              
3500             sub dumpable_template
3501             {
3502 3     3 1 20 my ( $self ) = @_;
3503 3         5 my ( $lineno, $ret, %instr_names );
3504              
3505 3         6 $ret = '';
3506 3         4 $lineno = 0;
3507 3         28 %instr_names = (
3508             (LITERAL) => 'literal',
3509             (EXPR) => 'expr',
3510             (JUMP) => 'jump',
3511             (JUMP_IF) => 'jump_if',
3512             (FOR) => 'for',
3513             (END_FOR) => 'end_for',
3514             (CONTEXT_PUSH) => 'context_push',
3515             (CONTEXT_POP) => 'context_pop',
3516             );
3517              
3518 3         6 foreach my $line ( @{$self->{ template }->{ program }} )
  3         10  
3519             {
3520 29         1798 my ( $instr, $file );
3521              
3522 29         72 $file = $self->{ template }->{ files }->[ $line->[ 1 ][ 0 ] ];
3523 29 50       153 $file = 'template-string' if $file =~ m{^string:///};
3524 29   33     188 $ret .= sprintf( "%04d: [%-20s %3d %3d][%-12s] ", $lineno++,
3525             $file, $line->[ 1 ][ 1 ], $line->[ 1 ][ 2 ],
3526             $instr_names{ $line->[ 0 ] } || $line->[ 0 ] );
3527              
3528 29         50 $instr = $line->[ 0 ];
3529 29 100       80 if( $instr == LITERAL )
    100          
    100          
    100          
    100          
    50          
    0          
    0          
3530             {
3531             # $ret .= "\"$line->[2]\"\n";
3532 17         36 $ret .= _tinydump( $line->[ 2 ] ) . "\n";
3533             }
3534             elsif( $instr == EXPR )
3535             {
3536 5 50       11 $ret .= _tinydump( $line->[ 2 ] ) .
3537             ( $line->[ 3 ] ? " (void)" : "" ). "\n";
3538             }
3539             elsif( $instr == JUMP )
3540             {
3541 2         8 $ret .= "$line->[2]\n";
3542             }
3543             elsif( $instr == JUMP_IF )
3544             {
3545 1         5 $ret .= $line->[ 2 ] . ' unless ' .
3546             _tinydump( $line->[ 3 ] ) . "\n";
3547             }
3548             elsif( $instr == FOR )
3549             {
3550 2         8 $ret .= "$line->[ 3 ] in " . _tinydump( $line->[ 4 ] ) .
3551             " then $line->[ 2 ]";
3552 2 100       182 $ret .= " (no special-vars)" unless $line->[ 5 ];
3553 2         6 $ret .= "\n";
3554             }
3555             elsif( $instr == END_FOR )
3556             {
3557 2         8 $ret .= "$line->[ 3 ] in " . _tinydump( $line->[ 4 ] ) .
3558             " repeat $line->[ 2 ]\n";
3559             }
3560             elsif( $instr == CONTEXT_PUSH )
3561             {
3562 0         0 $ret .= "context push of " . _tinydump( $line->[ 2 ] ) . "\n";
3563             }
3564             elsif( $instr == CONTEXT_POP )
3565             {
3566 0         0 $ret .= "context pop\n";
3567             }
3568             # TODO: local syntax support.
3569             }
3570              
3571 3         292 return( $ret );
3572             }
3573              
3574             #sub _decompile_template
3575             #{
3576             # my ( $self ) = @_;
3577             # my ( $lineno, $ret );
3578             #
3579             # $ret = '';
3580             # $lineno = 0;
3581             #
3582             # foreach my $line ( @{$self->{ template }->{ program }} )
3583             # {
3584             # my ( $instr );
3585             #
3586             # $instr = $line->[ 0 ];
3587             # if( $instr == LITERAL )
3588             # {
3589             # $ret .= ( $line->[ 2 ] =~ /^$/ ) ?
3590             # "<: empty literal :>" : $line->[ 2 ];
3591             # next;
3592             # }
3593             # $ret .= "<: $instr ";
3594             # if( $instr == EXPR )
3595             # {
3596             # my ( $dump );
3597             #
3598             # $dump = Data::Dumper::Dumper( $line->[ 2 ] );
3599             # $dump =~ s/^\$VAR1 = //;
3600             # $dump =~ s/;\n$//;
3601             # $ret .= $line->[ 2 ]->[ 1 ] . " ($dump)";
3602             # }
3603             # elsif( $instr == JUMP )
3604             # {
3605             # $ret .= "$line->[2]";
3606             # }
3607             # elsif( $instr == JUMP_IF )
3608             # {
3609             # $ret .= $line->[ 2 ] .
3610             # ( $line->[ 4 ] ? ' unless ' : ' if ' ) .
3611             # "$line->[3]";
3612             # }
3613             # elsif( $instr == FOR )
3614             # {
3615             # $ret .= "$line->[ 3 ] in $line->[ 4 ] then $line->[ 2 ]";
3616             # }
3617             # elsif( $instr == END_FOR )
3618             # {
3619             # $ret .= "$line->[ 3 ] in $line->[ 4 ] repeat $line->[ 2 ]";
3620             # }
3621             # elsif( $instr == CONTEXT_PUSH )
3622             # {
3623             # my ( $dump );
3624             #
3625             # $dump = defined( $line->[ 2 ] ) ? Data::Dumper::Dumper( $line->[ 2 ] ) : 'undef';
3626             # $dump =~ s/^\$VAR1 = //;
3627             # $dump =~ s/;\n$//;
3628             # $dump =~ s/\s+/ /g;
3629             # $ret .= "context push of $dump";
3630             # }
3631             # elsif( $instr == CONTEXT_POP )
3632             # {
3633             # $ret = substr( $ret, 0, -1 );
3634             # }
3635             ## TODO: support for local syntax
3636             # else
3637             # {
3638             # $ret .= "(unhandled by decompile)";
3639             # }
3640             # $ret .= " :>";
3641             # }
3642             #
3643             # return( $ret );
3644             #}
3645              
3646             1;
3647              
3648             __END__