File Coverage

blib/lib/Template/Sandbox.pm
Criterion Covered Total %
statement 1088 1134 95.9
branch 566 636 88.9
condition 188 233 80.6
subroutine 89 91 97.8
pod 47 47 100.0
total 1978 2141 92.3


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