File Coverage

blib/lib/Template/Alloy.pm
Criterion Covered Total %
statement 548 608 90.1
branch 392 502 78.0
condition 228 337 67.6
subroutine 40 42 95.2
pod 21 28 75.0
total 1229 1517 81.0


line stmt bran cond sub pod time code
1             package Template::Alloy;
2              
3             ###----------------------------------------------------------------###
4             # See the perldoc in Template/Alloy.pod #
5             # Copyright 2007 - 2013 - Paul Seamons #
6             # Distributed under the Perl Artistic License without warranty #
7             ###----------------------------------------------------------------###
8              
9 10     10   17068 use strict;
  10         24  
  10         725  
10 10     10   62 use warnings;
  10         23  
  10         367  
11 10     10   302 use 5.006;
  10         36  
  10         422  
12 10     10   11008 use Template::Alloy::Exception;
  10         32  
  10         309  
13 10     10   12162 use Template::Alloy::Operator qw(play_operator define_operator);
  10         36  
  10         1300  
14 10     10   15096 use Template::Alloy::VMethod qw(define_vmethod $SCALAR_OPS $ITEM_OPS $ITEM_METHODS $FILTER_OPS $LIST_OPS $HASH_OPS $VOBJS);
  10         31  
  10         2401  
15              
16 10     10   77 use vars qw($VERSION);
  10         23  
  10         542  
17             BEGIN {
18 10     10   9717 $VERSION = '1.020';
19             };
20             our $QR_PRIVATE = qr/^[_.]/;
21             our $WHILE_MAX = 1000;
22             our $MAX_EVAL_RECURSE = 50;
23             our $MAX_MACRO_RECURSE = 50;
24             our $STAT_TTL = 1;
25             our $QR_INDEX = '(?:\d*\.\d+ | \d+)';
26             our @CONFIG_COMPILETIME = qw(SYNTAX CACHE_STR_REFS ANYCASE INTERPOLATE PRE_CHOMP POST_CHOMP ENCODING
27             SEMICOLONS V1DOLLAR V2PIPE V2EQUALS AUTO_EVAL SHOW_UNDEFINED_INTERP AUTO_FILTER);
28             our @CONFIG_RUNTIME = qw(ADD_LOCAL_PATH CALL_CONTEXT DUMP VMETHOD_FUNCTIONS STRICT);
29             our $EVAL_CONFIG = {map {$_ => 1} @CONFIG_COMPILETIME, @CONFIG_RUNTIME};
30             our $EXTRA_COMPILE_EXT = '.sto';
31             our $PERL_COMPILE_EXT = '.pl';
32             our $JS_COMPILE_EXT = '.js';
33             our $GLOBAL_CACHE = {};
34              
35             ###----------------------------------------------------------------###
36              
37             our $AUTOROLE = {
38             Compile => [qw(load_perl compile_template compile_tree compile_expr)],
39             HTE => [qw(parse_tree_hte param output register_function clear_param query new_file new_scalar_ref new_array_ref new_filehandle)],
40             Parse => [qw(parse_tree parse_expr apply_precedence parse_args dump_parse_tree dump_parse_expr define_directive define_syntax)],
41             Play => [qw(play_tree _macro_sub)],
42             Stream => [qw(stream_tree)],
43             JS => [qw(load_js compile_template_js compile_tree_js play_js js_context process_js parse_tree_js process_jsr parse_tree_jsr)],
44             TT => [qw(parse_tree_tt3 process)],
45             Tmpl => [qw(parse_tree_tmpl set_delimiters set_strip set_value set_values parse_string set_dir parse_file loop_iteration fetch_loop_iteration)],
46             Velocity => [qw(parse_tree_velocity merge)],
47             };
48             my $ROLEMAP = { map { my $type = $_; map { ($_ => $type) } @{ $AUTOROLE->{$type} } } keys %$AUTOROLE };
49             my %STANDIN = ('Template' => 'TT', 'Template::Toolkit' => 'TT', 'HTML::Template' => 'HTE', 'HTML::Template::Expr' => 'HTE', 'Text::Tmpl' => 'Tmpl');
50              
51             our $AUTOLOAD;
52             sub AUTOLOAD {
53 43     43   991 my $self = shift;
54 43 100 66     841 my $meth = ($AUTOLOAD && $AUTOLOAD =~ /::(\w+)$/) ? $1 : $self->throw('autoload', "Invalid method $AUTOLOAD");
55 41 100       169 if (! $self->can($meth)) {
56 1         6 require Carp;
57 1         174 Carp::croak("Can't locate object method \"$meth\" via package ".ref($self));
58             }
59 40         264 return $self->$meth(@_);
60             }
61              
62             sub can {
63 42     42 0 850 my ($self, $meth) = @_;
64 42 100       7030 __PACKAGE__->import($ROLEMAP->{$meth}) if $ROLEMAP->{$meth};
65 42         556 return $self->SUPER::can($meth);
66             }
67              
68 0     0   0 sub DESTROY {}
69              
70             sub import {
71 126     126   15475 my $class = shift;
72 126         335 foreach my $item (@_) {
73 78 100       415 next if $item =~ /^(load|1)$/i;
74 72 100       269 if (lc $item eq 'all') {
75 2         8 local $AUTOROLE->{'JS'}; delete $AUTOROLE->{'JS'};
  2         6  
76 2         12 return $class->import(keys %$AUTOROLE);
77             }
78              
79 70         102 my $type;
80 70 100       254 if ($type = $STANDIN{$item}) {
81 7         42 (my $file = "$item.pm") =~ s|::|/|g;
82 7 100 66     77 if (! $INC{$file} || ! $item->isa(__PACKAGE__)) {
83 4 100       15 if ($INC{$file}) { require Carp; Carp::croak("Class $item is already loaded - can't override") }
  1         10  
  1         260  
84 3         313 eval "{package $item; our \@ISA = qw(".__PACKAGE__.");}";
85 3         15 $INC{$file} = __FILE__;
86 3 50       14 next if ! $AUTOROLE->{$type}; # already imported
87             }
88             }
89 69 100 66     440 $type ||= $AUTOROLE->{$item} ? $item : $ROLEMAP->{$item} || do { require Carp; Carp::croak("Invalid import option \"$item\"") };
      66        
90              
91 68         185 my $pkg = __PACKAGE__."::$type";
92 68         376 (my $file = "$pkg.pm") =~ s|::|/|g;
93 68         85204 require $file;
94              
95 10     10   69 no strict 'refs';
  10         20  
  10         108584  
96 68         477 *{__PACKAGE__."::$_"} = \&{"$pkg\::$_"} for @{ $AUTOROLE->{$type} };
  68         402  
  211         1302  
  211         863  
97 68         404 $AUTOROLE->{$type} = [];
98             }
99 122         40602 return 1;
100             }
101              
102             ###----------------------------------------------------------------###
103              
104             sub new {
105 4301     4301 1 3134684 my $class = shift;
106 4301 100       25819 my $args = ref($_[0]) ? { %{ shift() } } : {@_};
  1         5  
107              
108             ### allow for lowercase args
109 4301 100       17008 if (my @keys = grep {/^[a-z][a-z_]+$/} keys %$args) {
  5377         29014  
110 253         599 @{ $args }{ map { uc $_ } @keys } = delete @{ $args }{ @keys };
  253         996  
  753         1557  
  253         706  
111             }
112              
113 4301         19596 return bless $args, $class;
114             }
115              
116             ###----------------------------------------------------------------###
117              
118 0     0 0 0 sub run { shift->process_simple(@_) }
119              
120             sub process_simple {
121 773     773 1 3105 my $self = shift;
122 773   100     1907 my $in = shift || die "Missing input";
123 772   100     2010 my $swap = shift || die "Missing variable hash";
124 771   66     2506 my $out = shift || ($self->{'STREAM'} ? \ "" : die "Missing output string ref");
125 770         1355 delete $self->{'error'};
126              
127 770         1500 eval {
128 770         1390 delete $self->{'_debug_off'};
129 770         1108 delete $self->{'_debug_format'};
130 770         2040 local $self->{'_start_top_level'} = 1;
131 770         2167 $self->_process($in, $swap, $out);
132             };
133 770 100       2173 if (my $err = $@) {
134 40 50 0     124 if ($err->type !~ /stop|return|next|last|break/) {
    0          
135 40         100 $self->{'error'} = $err;
136 40 50       130 die $err if $self->{'RAISE_ERROR'};
137 40         350 return;
138             } elsif ($err->type eq 'return' && UNIVERSAL::isa($err->info, 'HASH')) {
139 0         0 return $err->info->{'return_val'};
140             }
141             }
142 730         5767 return 1;
143             }
144              
145             sub _process {
146 5350     5350   9616 my $self = shift;
147 5350         7482 my $file = shift;
148 5350   100     23287 local $self->{'_vars'} = shift || {};
149 5350   66     14962 my $out_ref = shift || $self->throw('undef', "Missing output ref");
150 5349         14794 local $self->{'_top_level'} = delete $self->{'_start_top_level'};
151 5349         9391 my $i = length $$out_ref;
152              
153             ### parse and execute
154 5349         25515 my $doc;
155 5349         7050 eval {
156 5349 100       19822 $doc = (ref($file) eq 'HASH') ? $file : $self->load_template($file);
157              
158             ### prevent recursion
159 5173 100 100     38493 $self->throw('file', "recursion into '$doc->{name}'")
      100        
160             if ! $self->{'RECURSION'} && $self->{'_in'}->{$doc->{'name'}} && $doc->{'name'} ne 'input text';
161              
162 5167         17511 local $self->{'_in'}->{$doc->{'name'}} = 1;
163 5167         11733 local $self->{'_component'} = $doc;
164 5167 100       17006 local $self->{'_template'} = $self->{'_top_level'} ? $doc : $self->{'_template'};
165 5167         8998 local @{ $self }{@CONFIG_RUNTIME} = @{ $self }{@CONFIG_RUNTIME};
  5167         43138  
  5167         18015  
166              
167             ### run the document however we can
168 5167 100       29405 if ($self->{'STREAM'}) {
    50          
    100          
    100          
169 1356 50       4130 $self->throw('process', 'No _tree found') if ! $doc->{'_tree'};
170 1356         6074 $self->stream_tree($doc->{'_tree'});
171             } elsif ($self->{'COMPILE_JS'}) {
172 0         0 $self->play_js($doc, $out_ref);
173             } elsif ($doc->{'_perl'}) {
174 1869         58130 $doc->{'_perl'}->{'code'}->($self, $out_ref);
175             } elsif ($doc->{'_tree'}) {
176 1941         29172 $self->play_tree($doc->{'_tree'}, $out_ref);
177             } else {
178 1         3 $self->throw('process', 'No _perl and no _tree found');
179             }
180              
181             ### trim whitespace from the beginning and the end of a block or template
182 4972 100       33806 if ($self->{'TRIM'}) {
183 27         140 substr($$out_ref, $i, length($$out_ref) - $i) =~ s{ \s+ $ }{}x; # tail first
184 27         190 substr($$out_ref, $i, length($$out_ref) - $i) =~ s{ ^ \s+ }{}x;
185             }
186             };
187              
188             ### handle exceptions
189 5349 100       16086 if (my $err = $@) {
190 377 100       2034 $err = $self->exception('undef', $err) if ! UNIVERSAL::can($err, 'type');
191 377 100 66     3186 $err->doc($doc) if $doc && $err->can('doc') && ! $err->doc;
      100        
192 377 100       2659 die $err if ! $self->{'_top_level'};
193 273 100 66     1009 die $err if $err->type ne 'stop' && ($err->type ne 'return' || $err->info);
      66        
194             }
195              
196 5010         26742 return 1;
197             }
198              
199             ###----------------------------------------------------------------###
200              
201             sub load_template {
202 5381     5381 1 12358 my ($self, $file) = @_;
203 5381 100       22960 $self->throw('undef', 'Undefined file passed to load_template') if ! defined $file;
204              
205 5380   66     31338 my $docs = $self->{'GLOBAL_CACHE'} || ($self->{'_documents'} ||= {});
206 5380 100       13456 $docs = $GLOBAL_CACHE if ! ref $docs;
207              
208             ### looks like a scalar ref
209 5380         7489 my $doc;
210 5380 100       16226 if (ref $file) {
    100          
    100          
    100          
211 4581 100       14725 return $file if ref $file eq 'HASH';
212              
213 4580 100 66     18169 if (! defined($self->{'CACHE_STR_REFS'}) || $self->{'CACHE_STR_REFS'}) {
214 4577         12150 my $_file = $self->string_id($file);
215 4577 100       12941 if ($docs->{$_file}) { # no-ttl necessary
216 193         388 $doc = $docs->{$_file};
217 193 100 100     966 $doc->{'_perl'} = $self->load_perl($doc) if ! $doc->{'_perl'} && $self->{'COMPILE_PERL'}; # second hit
218 193         631 return $doc;
219             }
220 4384         17375 $doc->{'_filename'} = $_file;
221             } else {
222 3 100       16 $doc->{'_no_perl'} = $self->{'FORCE_STR_REF_PERL'} ? 0 : 1;
223             }
224 4387         10458 $doc->{'_is_str_ref'} = 1;
225 4387         7935 $doc->{'_content'} = $file;
226 4387         7527 $doc->{'name'} = 'input text';
227 4387         8851 $doc->{'modtime'} = time;
228              
229             ### looks like a previously cached document
230             } elsif ($docs->{$file}) {
231 19         42 $doc = $docs->{$file};
232 19 50 33     153 if (time - $doc->{'cache_time'} < ($self->{'STAT_TTL'} || $STAT_TTL) # don't stat more than once a second
      33        
233             || $doc->{'modtime'} == (stat $doc->{'_filename'})[9]) { # otherwise see if the file was modified
234 19 100 100     114 $doc->{'_perl'} = $self->load_perl($doc) if ! $doc->{'_perl'} && $self->{'COMPILE_PERL'}; # second hit
235 19         50 return $doc;
236             }
237 0         0 delete @$doc{qw(_tree modtime _content _line_offsets _perl _js)};
238              
239             ### looks like a previously cached not-found
240             } elsif ($self->{'_not_found'}->{$file}) {
241 13         33 $doc = $self->{'_not_found'}->{$file};
242 13 100 66     102 if (time - $doc->{'cache_time'} < ($self->{'NEGATIVE_STAT_TTL'} || $self->{'STAT_TTL'} || $STAT_TTL)) { # negative cache for a second
243 7         35 die $doc->{'exception'};
244             }
245 6         15 delete $self->{'_not_found'}->{$file}; # clear cache on failure
246              
247             ### looks like a block passed in at runtime
248             } elsif ($self->{'BLOCKS'}->{$file}) {
249 235         588 my $block = $self->{'BLOCKS'}->{$file};
250 235 100       1021 $block = $block->() if UNIVERSAL::isa($block, 'CODE');
251 235 100       735 if (! UNIVERSAL::isa($block, 'HASH')) {
252 4 100       16 $self->throw('block', "Unsupported BLOCK type \"$block\"") if ref $block;
253 3   66     6 $block = eval { $self->load_template(\$block) } || $self->throw('block', 'Parse error on predefined block');
254             }
255 233 100 100     1521 $doc->{'name'} = ($block->{'name'} && $block->{'name'} ne 'input text') ? $block->{'name'} : $file;
256 233 100       726 $doc->{'_filename'} = $block->{'_filename'} if $block->{'_filename'};
257 233 100       650 if ($block->{'_perl'}) {
    100          
258 81         183 $doc->{'_perl'} = $block->{'_perl'};
259             } elsif ($block->{'_tree'}) {
260 151         285 $doc->{'_tree'} = $block->{'_tree'};
261             } else {
262 1         6 $self->throw('block', "Invalid block definition (missing tree)");
263             }
264 232         503 return $doc;
265             }
266              
267             ### lookup the filename
268 4925 100 66     16593 if (! $doc->{'_filename'} && ! ref $file) {
269 538         1656 $doc->{'name'} = $file;
270 538         890 $doc->{'_filename'} = eval { $self->include_filename($file) };
  538         1527  
271 538 100       2094 if (my $err = $@) {
272             ### allow for blocks in other files
273 71 100 66     461 if ($self->{'EXPOSE_BLOCKS'} && ! $self->{'_looking_in_block_file'}) {
    50          
274 18         51 local $self->{'_looking_in_block_file'} = 1;
275 18         36 my $block_name = '';
276 18         130 OUTER: while ($file =~ s|/([^/.]+)$||) {
277 12 50       47 $block_name = length($block_name) ? "$1/$block_name" : $1;
278 12   50     21 my $ref = eval { $self->load_template($file) } || next;
279 12         42 my $_tree = $ref->{'_tree'};
280 12         31 foreach my $node (@$_tree) {
281 30 50       69 last if ! ref $node;
282 30 100       94 next if $node->[0] eq 'META';
283 18 50       55 last if $node->[0] ne 'BLOCK';
284 18 100       50 next if $block_name ne $node->[3];
285 12         30 $doc->{'_tree'} = $node->[4];
286 12         24 @{$doc}{qw(modtime _content)} = @{$ref}{qw(modtime _content)};
  12         35  
  12         36  
287 12 50 66     107 $doc->{'_perl'} = {
      66        
288             meta => {},
289             blocks => {},
290             code => $ref->{'_perl'}->{'blocks'}->{$block_name}->{'_perl'}->{'code'},
291             } if $ref->{'_perl'} && $ref->{'_perl'}->{'blocks'} && $ref->{'_perl'}->{'blocks'}->{$block_name};
292 12 50 33     49 $doc->{'_js'} = $self->load_js($doc) if $self->{'COMPILE_JS'} && $ref->{'_js'}; # have to regenerate because block is buried in js
293 12         83 return $doc;
294             }
295             }
296             } elsif ($self->{'DEFAULT'}) {
297 0 0       0 $err = '' if ($doc->{'_filename'} = eval { $self->include_filename($self->{'DEFAULT'}) });
  0         0  
298             }
299 59 50       208 if ($err) {
300             ### cache the negative error
301 59 50 66     282 if (! defined($self->{'NEGATIVE_STAT_TTL'}) || $self->{'NEGATIVE_STAT_TTL'}) {
302 59 50       259 $err = $self->exception('undef', $err) if ! UNIVERSAL::can($err, 'type');
303 59         250 $self->{'_not_found'}->{$file} = {
304             cache_time => time,
305             exception => $self->exception($err->type, $err->info." (cached)"),
306             };
307             }
308 59         320 die $err;
309             }
310             }
311             }
312              
313             ### return perl - if they want perl - otherwise - the ast
314 4854 100 100     41675 if (! $doc->{'_no_perl'} && $self->{'COMPILE_PERL'} && ($self->{'COMPILE_PERL'} ne '2' || $self->{'_tree'})) {
    50 66        
      66        
315 1742         7823 $doc->{'_perl'} = $self->load_perl($doc);
316             } elsif ($self->{'COMPILE_JS'}) {
317 0         0 $self->load_js($doc);
318             } else {
319 3112         9039 $doc->{'_tree'} = $self->load_tree($doc);
320             }
321              
322             ### cache parsed_tree in memory unless asked not to do so
323 4721 100 66     20317 if (! defined($self->{'CACHE_SIZE'}) || $self->{'CACHE_SIZE'}) {
324 4469         9558 $doc->{'cache_time'} = time;
325 4469 100       12393 if (ref $file) {
326 4024 100       19864 $docs->{$doc->{'_filename'}} = $doc if $doc->{'_filename'};
327             } else {
328 445   33     2267 $docs->{$file} ||= $doc;
329             }
330              
331             ### allow for config option to keep the cache size down
332 4469 50       12583 if ($self->{'CACHE_SIZE'}) {
333 0 0       0 if (scalar(keys %$docs) > $self->{'CACHE_SIZE'}) {
334 0         0 my $n = 0;
335 0         0 foreach my $file (sort {$docs->{$b}->{'cache_time'} <=> $docs->{$a}->{'cache_time'}} keys %$docs) {
  0         0  
336 0 0       0 delete($docs->{$file}) if ++$n > $self->{'CACHE_SIZE'};
337             }
338             }
339             }
340             }
341              
342 4721         12467 return $doc;
343             }
344              
345             sub string_id {
346 4578     4578 0 10856 my ($self, $ref) = @_;
347 4578         38408 require Digest::MD5;
348             my $str = ref($self)
349             && $self->{'ENCODING'} # ENCODING is defined
350 4578 100 66     34260 && eval { require Encode } # Encode.pm is available
351             && defined &Encode::encode
352             ? Encode::encode($self->{'ENCODING'}, $$ref)
353             : $$ref;
354 4578         26499 my $sum = Digest::MD5::md5_hex($str);
355 4578         20067 return 'Alloy_str_ref_cache/'.substr($sum,0,3).'/'.$sum;
356             }
357              
358             sub load_tree {
359 4853     4853 1 9015 my ($self, $doc) = @_;
360              
361             ### first look for a compiled optree
362 4853 100       13875 if ($doc->{'_filename'}) {
363 4850   66     21764 $doc->{'modtime'} ||= (stat $doc->{'_filename'})[9];
364 4850 100 100     26831 if ($self->{'COMPILE_DIR'} || $self->{'COMPILE_EXT'}) {
365 16         37 my $file = $doc->{'_filename'};
366 16 100       58 if ($self->{'COMPILE_DIR'}) {
    100          
367 8 50       35 $file =~ y|:|/| if $^O eq 'MSWin32';
368 8         28 $file = $self->{'COMPILE_DIR'} .'/'. $file;
369             } elsif ($doc->{'_is_str_ref'}) {
370 2   50     7 $file = ($self->include_paths->[0] || '.') .'/'. $file;
371             }
372 16 100       60 $file .= $self->{'COMPILE_EXT'} if defined($self->{'COMPILE_EXT'});
373 16 50       54 $file .= $EXTRA_COMPILE_EXT if defined $EXTRA_COMPILE_EXT;
374              
375 16 50 33     235 if (-e $file && ($doc->{'_is_str_ref'} || (stat $file)[9] == $doc->{'modtime'})) {
      66        
376 1         9 require Storable;
377 1         7 return Storable::retrieve($file);
378             }
379 15         43 $doc->{'_storable_filename'} = $file;
380             }
381             }
382              
383             ### no cached tree - we will need to load our own
384 4852   66     13833 $doc->{'_content'} ||= $self->slurp($doc->{'_filename'});
385              
386 4852 100       13958 if ($self->{'CONSTANTS'}) {
387 33   50     159 my $key = $self->{'CONSTANT_NAMESPACE'} || 'constants';
388 33   33     409 $self->{'NAMESPACE'}->{$key} ||= $self->{'CONSTANTS'};
389             }
390              
391 4852         12439 local $self->{'_component'} = $doc;
392             my $tree = eval { $self->parse_tree($doc->{'_content'}) }
393 4852   66     8084 || do { my $e = $@; $e->doc($doc) if UNIVERSAL::can($e, 'doc') && ! $e->doc; die $e }; # errors die
394              
395             ### save a cache on the fileside as asked
396 4719 100       14621 if ($doc->{'_storable_filename'}) {
397 15         33 my $dir = $doc->{'_storable_filename'};
398 15         110 $dir =~ s|/[^/]+$||;
399 15 100       214 if (! -d $dir) {
400 10         84 require File::Path;
401 10         2749 File::Path::mkpath($dir);
402             }
403 15         1327 require Storable;
404 15         4095 Storable::store($tree, $doc->{'_storable_filename'});
405 15         3200 utime $doc->{'modtime'}, $doc->{'modtime'}, $doc->{'_storable_filename'};
406             }
407              
408 4719         21156 return $tree;
409             }
410              
411             ###----------------------------------------------------------------###
412              
413             ### allow for resolving full expression ASTs
414             sub play_expr {
415 28489 100   28489 1 111717 return $_[1] if ! ref $_[1]; # allow for the parse tree to store literals
416              
417 18936         26286 my $self = shift;
418 18936         25662 my $var = shift;
419 18936   100     77850 my $ARGS = shift || {};
420 18936         23226 my $i = 0;
421              
422             ### determine the top level of this particular variable access
423 18936         38271 my $ref;
424 18936         31355 my $name = $var->[$i++];
425 18936         26852 my $args = $var->[$i++];
426 18936 100       54213 if (ref $name) {
    50          
427 9292 100       23898 if (! defined $name->[0]) { # operator
428 9118 100 100     26427 return $self->play_operator($name) if wantarray && $name->[1] eq '..';
429 8926 100       37990 $ref = ($name->[1] eq '-temp-') ? $name->[2] : $self->play_operator($name);
430             } else { # a named variable access (ie via $name.foo)
431 174         560 $name = $self->play_expr($name);
432 174 50       502 if (defined $name) {
433 174 100 66     1154 return if $QR_PRIVATE && $name =~ $QR_PRIVATE; # don't allow vars that begin with _
434 171 50 66     831 return \$self->{'_vars'}->{$name} if $i >= $#$var && $ARGS->{'return_ref'} && ! ref $self->{'_vars'}->{$name};
      33        
435 171         424 $ref = $self->{'_vars'}->{$name};
436             }
437             }
438             } elsif (defined $name) {
439 9644 100 100     75314 return if $QR_PRIVATE && $name =~ $QR_PRIVATE; # don't allow vars that begin with _
440 9629 100 100     43862 return \$self->{'_vars'}->{$name} if $i >= $#$var && $ARGS->{'return_ref'} && ! ref $self->{'_vars'}->{$name};
      100        
441 9623         25514 $ref = $self->{'_vars'}->{$name};
442 9623 100       23256 if (! defined $ref) {
443 873 100 100     5269 $ref = ($name eq 'template' || $name eq 'component') ? $self->{"_$name"} : $VOBJS->{$name};
444 873 100 66     6868 $ref = $ITEM_METHODS->{$name} || $ITEM_OPS->{$name} if ! $ref && (! defined($self->{'VMETHOD_FUNCTIONS'}) || $self->{'VMETHOD_FUNCTIONS'});
      100        
      66        
445 873 100 100     4094 $ref = $self->{'_vars'}->{lc $name} if ! defined $ref && $self->{'LOWER_CASE_VAR_FALLBACK'};
446             }
447             }
448              
449 18720         23661 my %seen_filters;
450 18720         47429 while (defined $ref) {
451              
452             ### check at each point if the returned thing was a code
453 21271 100       74099 if (UNIVERSAL::isa($ref, 'CODE')) {
454 547 100 100     3086 return $ref if $i >= $#$var && $ARGS->{'return_ref'};
455 532 100       1645 my @args = $args ? map { $self->play_expr($_) } @$args : ();
  207         670  
456 532   100     2227 my $type = $self->{'CALL_CONTEXT'} || '';
457 532 100       1443 if ($type eq 'item') {
458 39         129 $ref = $ref->(@args);
459             } else {
460 493         2548 my @results = $ref->(@args);
461 490 100       2918 if ($type eq 'list') {
    100          
    50          
462 33         93 $ref = \@results;
463             } elsif (defined $results[0]) {
464 453 100       1800 $ref = ($#results > 0) ? \@results : $results[0];
465             } elsif (defined $results[1]) {
466 0         0 die $results[1]; # TT behavior - why not just throw ?
467             } else {
468 4         6 $ref = undef;
469 4         11 last;
470             }
471             }
472             }
473              
474             ### descend one chained level
475 21249 100       51839 last if $i >= $#$var;
476 3453 50       12386 my $was_dot_call = $ARGS->{'no_dots'} ? 1 : $var->[$i++] eq '.';
477 3453         6896 $name = $var->[$i++];
478 3453         5246 $args = $var->[$i++];
479              
480             ### allow for named portions of a variable name (foo.$name.bar)
481 3453 100       8970 if (ref $name) {
482 60 50       236 if (ref($name) eq 'ARRAY') {
483 60         172 $name = $self->play_expr($name);
484 60 50 33     1452 if (! defined($name) || ($QR_PRIVATE && $name =~ $QR_PRIVATE) || $name =~ /^\./) {
      33        
      33        
485 0         0 $ref = undef;
486 0         0 last;
487             }
488             } else {
489 0         0 die "Shouldn't get a ". ref($name) ." during a vivify on chain";
490             }
491             }
492 3453 100 100     28604 if (! defined $name || ($QR_PRIVATE && $name =~ $QR_PRIVATE)) { # don't allow vars that begin with _
      33        
493 3         7 $ref = undef;
494 3         7 last;
495             }
496              
497             ### allow for scalar and filter access (this happens for every non virtual method call)
498 3450 100       9273 if (! ref $ref) {
499 939 100 100     5019 if ($ITEM_METHODS->{$name}) { # normal scalar op
    100          
    100          
    100          
500 223 100       826 $ref = $ITEM_METHODS->{$name}->($self, $ref, $args ? map { $self->play_expr($_) } @$args : ());
  60         194  
501              
502             } elsif ($ITEM_OPS->{$name}) { # normal scalar op
503 665 100       2936 $ref = $ITEM_OPS->{$name}->($ref, $args ? map { $self->play_expr($_) } @$args : ());
  443         1175  
504              
505             } elsif ($LIST_OPS->{$name}) { # auto-promote to list and use list op
506 9 50       60 $ref = $LIST_OPS->{$name}->([$ref], $args ? map { $self->play_expr($_) } @$args : ());
  0         0  
507              
508             } elsif (my $filter = $self->{'FILTERS'}->{$name} # filter configured in Template args
509             || $FILTER_OPS->{$name} # predefined filters in Alloy
510             || (UNIVERSAL::isa($name, 'CODE') && $name) # looks like a filter sub passed in the stash
511             || $self->list_filters->{$name}) { # filter defined in Template::Filters
512              
513 36 100 66     334 if (UNIVERSAL::isa($filter, 'CODE')) {
    50          
    100          
514 9         17 $ref = eval { $filter->($ref) }; # non-dynamic filter - no args
  9         25  
515 9 50       62 if (my $err = $@) {
516 0 0       0 $self->throw('filter', $err) if ! UNIVERSAL::can($err, 'type');
517 0         0 die $err;
518             }
519             } elsif (! UNIVERSAL::isa($filter, 'ARRAY')) {
520 0         0 $self->throw('filter', "invalid FILTER entry for '$name' (not a CODE ref)");
521              
522             } elsif (@$filter == 2 && UNIVERSAL::isa($filter->[0], 'CODE')) { # these are the TT style filters
523 9         15 eval {
524 9         17 my $sub = $filter->[0];
525 9 100       28 if ($filter->[1]) { # it is a "dynamic filter" that will return a sub
526 6 100       28 ($sub, my $err) = $sub->($self->context, $args ? map { $self->play_expr($_) } @$args : ());
  3         15  
527 6 50 33     56944 if (! $sub && $err) {
    50          
528 0 0       0 $self->throw('filter', $err) if ! UNIVERSAL::can($err, 'type');
529 0         0 die $err;
530             } elsif (! UNIVERSAL::isa($sub, 'CODE')) {
531 0 0       0 $self->throw('filter', "invalid FILTER for '$name' (not a CODE ref)")
532             if ! UNIVERSAL::can($sub, 'type');
533 0         0 die $sub;
534             }
535             }
536 9         39 $ref = $sub->($ref);
537             };
538 9 50       141 if (my $err = $@) {
539 0 0       0 $self->throw('filter', $err) if ! UNIVERSAL::can($err, 'type');
540 0         0 die $err;
541             }
542             } else { # this looks like our vmethods turned into "filters" (a filter stored under a name)
543 18 50       67 $self->throw('filter', 'Recursive filter alias \"$name\"') if $seen_filters{$name} ++;
544 18         54 $var = [$name, 0, '|', @$filter, @{$var}[$i..$#$var]]; # splice the filter into our current tree
  18         77  
545 18         30 $i = 2;
546             }
547 36 50 50     276 if (scalar keys %seen_filters
      66        
548             && $seen_filters{$var->[$i - 5] || ''}) {
549 0         0 $self->throw('filter', "invalid FILTER entry for '".$var->[$i - 5]."' (not a CODE ref)");
550             }
551             } else {
552 6         23 $ref = undef;
553             }
554              
555             } else {
556              
557             ### method calls on objects
558 2511 100 100     15434 if ($was_dot_call && UNIVERSAL::can($ref, 'can')) {
559 845 50 66     4285 return $ref if $i >= $#$var && $ARGS->{'return_ref'};
560 845   100     3431 my $type = $self->{'CALL_CONTEXT'} || '';
561 845 100       2164 my @args = $args ? map { $self->play_expr($_) } @$args : ();
  156         836  
562 845 100       2810 if ($type eq 'item') {
    100          
563 21         132 $ref = $ref->$name(@args);
564 21         331 next;
565             } elsif ($type eq 'list') {
566 21         129 $ref = [$ref->$name(@args)];
567 21         340 next;
568             }
569 803         1222 my @results = eval { $ref->$name(@args) };
  803         4156  
570 803 100       7602 if ($@) {
    50          
    0          
571 13         35 my $class = ref $ref;
572 13 50 66     352 die $@ if ref $@ || $@ !~ /Can\'t locate object method "\Q$name\E" via package "\Q$class\E"/ || $type eq 'list';
      66        
573             } elsif (defined $results[0]) {
574 790 100       1949 $ref = ($#results > 0) ? \@results : $results[0];
575 790         2677 next;
576             } elsif (defined $results[1]) {
577 0         0 die $results[1]; # TT behavior - why not just throw ?
578             } else {
579 0         0 $ref = undef;
580 0         0 last;
581             }
582             # didn't find a method by that name - so fail down to hash and array access
583             }
584              
585 1675 100       8500 if (UNIVERSAL::isa($ref, 'HASH')) {
    50          
586 1056 100 100     5834 if ($was_dot_call && exists($ref->{$name}) ) {
    100          
    100          
587 756 100 100     3900 return \ $ref->{$name} if $i >= $#$var && $ARGS->{'return_ref'} && ! ref $ref->{$name};
      66        
588 744         2740 $ref = $ref->{$name};
589             } elsif ($HASH_OPS->{$name}) {
590 171 100       736 $ref = $HASH_OPS->{$name}->($ref, $args ? map { $self->play_expr($_) } @$args : ());
  108         378  
591             } elsif ($ARGS->{'is_namespace_during_compile'}) {
592 6         32 return $var; # abort - can't fold namespace variable
593             } else {
594 123 100 100     691 return \ $ref->{$name} if $i >= $#$var && $ARGS->{'return_ref'};
595 120         413 $ref = undef;
596             }
597              
598             } elsif (UNIVERSAL::isa($ref, 'ARRAY')) {
599 619 100       8957 if ($name =~ m{ ^ -? $QR_INDEX $ }ox) {
    50          
600 155 100 100     1008 return \ $ref->[$name] if $i >= $#$var && $ARGS->{'return_ref'} && ! ref $ref->[$name];
      66        
601 149         597 $ref = $ref->[$name];
602             } elsif ($LIST_OPS->{$name}) {
603 464 100       2009 $ref = $LIST_OPS->{$name}->($ref, $args ? map { $self->play_expr($_) } @$args : ());
  179         514  
604             } else {
605 0         0 $ref = undef;
606             }
607             }
608             }
609              
610             } # end of while
611              
612 18627 100       38119 if (! defined $ref) {
613 831 100       2206 $self->strict_throw($var) if $self->{'STRICT'}; # will die
614 807 100       2175 die $self->tt_var_string($var)." is undefined\n" if $self->{'_debug_undef'};
615 801         2657 $ref = $self->undefined_any($var);
616             }
617              
618 18597         185193 return $ref;
619             }
620              
621             sub set_variable {
622 6083     6083 1 11865 my ($self, $var, $val, $ARGS) = @_;
623 6083   100     24573 $ARGS ||= {};
624 6083         8948 my $i = 0;
625              
626             ### allow for the parse tree to store literals - the literal is used as a name (like [% 'a' = 'A' %])
627 6083 100       22705 $var = [$var, 0] if ! ref $var;
628              
629             ### determine the top level of this particular variable access
630 6083         10976 my $ref = $var->[$i++];
631 6083         10226 my $args = $var->[$i++];
632 6083 100       19247 if (ref $ref) {
    50          
633             ### non-named types can't be set
634 18 50       71 return if ref($ref) ne 'ARRAY';
635 18 100       51 if (! defined $ref->[0]) {
636 6 50 33     50 return if ! $ref->[1] || $ref->[1] !~ /^[\$\@]\(\)$/; # do allow @( )
637 6         37 $ref = $self->play_operator($ref);
638             } else {
639             # named access (ie via $name.foo)
640 12         29 $ref = $self->play_expr($ref);
641 12 50 33     137 if (defined $ref && (! $QR_PRIVATE || $ref !~ $QR_PRIVATE)) { # don't allow vars that begin with _
      33        
642 12 100       31 if ($#$var <= $i) {
643 6         89 return $self->{'_vars'}->{$ref} = $val;
644             } else {
645 6   50     47 $ref = $self->{'_vars'}->{$ref} ||= {};
646             }
647             } else {
648 0         0 return;
649             }
650             }
651             } elsif (defined $ref) {
652 6065 100 100     60982 return if $QR_PRIVATE && $ref =~ $QR_PRIVATE; # don't allow vars that begin with _
653 6062 100       15314 if ($#$var <= $i) {
654 5880         58824 return $self->{'_vars'}->{$ref} = $val;
655             } else {
656 182   100     1029 $ref = $self->{'_vars'}->{$ref} ||= {};
657             }
658             }
659              
660 194         558 while (defined $ref) {
661              
662             ### check at each point if the returned thing was a code
663 256 100       1066 if (UNIVERSAL::isa($ref, 'CODE')) {
664 6   100     33 my $type = $self->{'CALL_CONTEXT'} || '';
665 6 50       22 my @args = $args ? map { $self->play_expr($_) } @$args : ();
  0         0  
666 6 50       20 if ($type eq 'item') {
667 0         0 $ref = $ref->(@args);
668             } else {
669 6         27 my @results = $ref->(@args);
670 6 100       40 if ($type eq 'list') {
    50          
    0          
671 3         19 $ref = \@results;
672             } elsif (defined $results[0]) {
673 3 50       15 $ref = ($#results > 0) ? \@results : $results[0];
674             } elsif (defined $results[1]) {
675 0         0 die $results[1]; # TT behavior - why not just throw ?
676             } else {
677 0         0 return;
678             }
679             }
680             }
681              
682             ### descend one chained level
683 256 50       703 last if $i >= $#$var;
684 256 100       930 my $was_dot_call = $ARGS->{'no_dots'} ? 1 : $var->[$i++] eq '.';
685 256         470 my $name = $var->[$i++];
686 256         373 my $args = $var->[$i++];
687              
688             ### allow for named portions of a variable name (foo.$name.bar)
689 256 100       664 if (ref $name) {
690 18 50       55 if (ref($name) eq 'ARRAY') {
691 18         43 $name = $self->play_expr($name);
692 18 50 33     266 if (! defined($name) || $name =~ /^[_.]/) {
693 0         0 return;
694             }
695             } else {
696 0         0 die "Shouldn't get a ".ref($name)." during a vivify on chain";
697             }
698             }
699 256 50 33     1775 if ($QR_PRIVATE && $name =~ $QR_PRIVATE) { # don't allow vars that begin with _
700 0         0 return;
701             }
702              
703             ### scalar access
704 256 50       1263 if (! ref $ref) {
    100          
705 0         0 return;
706              
707             ### method calls on objects
708             } elsif (UNIVERSAL::can($ref, 'can')) {
709 49         69 my $lvalueish;
710 49   100     235 my $type = $self->{'CALL_CONTEXT'} || '';
711 49 50       140 my @args = $args ? map { $self->play_expr($_) } @$args : ();
  0         0  
712 49 100       140 if ($i >= $#$var) {
713 40         50 $lvalueish = 1;
714 40         93 push @args, $val;
715             }
716 49 100       195 if ($type eq 'item') {
    100          
717 3         18 $ref = $ref->$name(@args);
718 3 50       65 return if $lvalueish;
719 0         0 next;
720             } elsif ($type eq 'list') {
721 3         23 $ref = [$ref->$name(@args)];
722 3 50       67 return if $lvalueish;
723 0         0 next;
724             }
725 43         104 my @results = eval { $ref->$name(@args) };
  43         329  
726 43 100       823 if (! $@) {
727 39 50       101 if (defined $results[0]) {
    0          
728 39 50       161 $ref = ($#results > 0) ? \@results : $results[0];
729             } elsif (defined $results[1]) {
730 0         0 die $results[1]; # TT behavior - why not just throw ?
731             } else {
732 0         0 return;
733             }
734 39 100       646 return if $lvalueish;
735 9         37 next;
736             }
737 4         12 my $class = ref $ref;
738 4 50 33     88 die $@ if ref $@ || $@ !~ /Can\'t locate object method "\Q$name\E" via package "\Q$class\E"/;
739             # fall on down to "normal" accessors
740             }
741              
742 207 100       798 if (UNIVERSAL::isa($ref, 'HASH')) {
    50          
743 189 100       475 if ($#$var <= $i) {
744 142         2808 return $ref->{$name} = $val;
745             } else {
746 47   100     246 $ref = $ref->{$name} ||= {};
747 47         144 next;
748             }
749              
750             } elsif (UNIVERSAL::isa($ref, 'ARRAY')) {
751 18 50       157 if ($name =~ m{ ^ -? $QR_INDEX $ }ox) {
752 18 100       42 if ($#$var <= $i) {
753 12         171 return $ref->[$name] = $val;
754             } else {
755 6   50     24 $ref = $ref->[$name] ||= {};
756 6         20 next;
757             }
758             } else {
759 0         0 return;
760             }
761              
762             }
763              
764             }
765              
766 0         0 return;
767             }
768              
769             ###----------------------------------------------------------------###
770              
771             sub _vars {
772 332     332   485 my $self = shift;
773 332 50       858 $self->{'_vars'} = shift if @_ == 1;
774 332   50     1758 return $self->{'_vars'} ||= {};
775             }
776              
777             sub include_filename {
778 559     559 1 1081 my ($self, $file) = @_;
779 559 50       2743 if ($file =~ m|^/|) {
    50          
780 0 0       0 $self->throw('file', "$file absolute paths are not allowed (set ABSOLUTE option)") if ! $self->{'ABSOLUTE'};
781 0 0       0 return $file if -e $file;
782             } elsif ($file =~ m{(^|/)\.\./}) {
783 0 0       0 $self->throw('file', "$file relative paths are not allowed (set RELATIVE option)") if ! $self->{'RELATIVE'};
784 0 0       0 return $file if -e $file;
785             }
786              
787 559         720 my @paths = @{ $self->include_paths };
  559         1527  
788 559 50 66     3318 if ($self->{'ADD_LOCAL_PATH'}
      66        
      33        
789             && $self->{'_component'}
790             && $self->{'_component'}->{'_filename'}
791             && $self->{'_component'}->{'_filename'} =~ m|^(.+)/[^/]+$|) {
792 60 100       246 ($self->{'ADD_LOCAL_PATH'} < 0) ? push(@paths, $1) : unshift(@paths, $1);
793             }
794              
795 559         1079 foreach my $path (@paths) {
796 600 100       15177 return "$path/$file" if -e "$path/$file";
797             }
798              
799 71         483 $self->throw('file', "$file: not found");
800             }
801              
802             sub include_paths {
803 562     562 0 1032 my $self = shift;
804 562   66     2102 return $self->{'INCLUDE_PATHS'} ||= do {
805             # TT does this everytime a file is looked up - we are going to do it just in time - the first time
806 426   100     1412 my $paths = $self->{'INCLUDE_PATH'} || ['.'];
807 426 50       2308 $paths = $paths->() if UNIVERSAL::isa($paths, 'CODE');
808 426 100       21598 $paths = $self->split_paths($paths) if ! UNIVERSAL::isa($paths, 'ARRAY');
809 426         2391 $paths; # return of the do
810             };
811             }
812              
813             sub split_paths {
814 548     548 1 964 my ($self, $path) = @_;
815 548 100       2566 return $path if UNIVERSAL::isa($path, 'ARRAY');
816 521   50     2316 my $delim = $self->{'DELIMITER'} || ':';
817 521 50 33     5365 $delim = ($delim eq ':' && $^O eq 'MSWin32') ? qr|:(?!/)| : qr|\Q$delim\E|;
818 521         4306 return [split $delim, "$path"]; # allow objects to stringify as necessary
819             }
820              
821             sub slurp {
822 490     490 1 907 my ($self, $file) = @_;
823 490 50       22974 open(my $fh, '<', $file) || $self->throw('file', "$file couldn't be opened: $!");
824 490         18834 read $fh, my $txt, -s $file;
825              
826 490 100       1453 if ($self->{'ENCODING'}) { # thanks to Carl Franks for this addition
827 3         8 eval { require Encode };
  3         27  
828 3 50 33     22 if ($@ || ! defined &Encode::decode) {
829 0         0 warn "Encode module not found, 'ENCODING' config only available on perl >= 5.7.3\n$@";
830             } else {
831 3         13 $txt = Encode::decode($self->{'ENCODING'}, $txt);
832             }
833             }
834              
835 490         8293 return \$txt;
836             }
837              
838 38     38 1 534 sub error { shift->{'error'} }
839              
840             sub exception {
841 535     535 1 957 my $self_or_class = shift;
842 535         854 my $type = shift;
843 535         807 my $info = shift;
844 535 100       3941 return $type if UNIVERSAL::can($type, 'type');
845 522 100       1416 if (ref($info) eq 'ARRAY') {
846 100 50       312 my $hash = ref($info->[-1]) eq 'HASH' ? pop(@$info) : {};
847 100 50 50     600 if (@$info >= 2 || scalar keys %$hash) {
    100          
848 0         0 my $i = 0;
849 0         0 $hash->{$_} = $info->[$_] for 0 .. $#$info;
850 0         0 $hash->{'args'} = $info;
851 0         0 $info = $hash;
852             } elsif (@$info == 1) {
853 87         249 $info = $info->[0];
854             } else {
855 13         32 $info = $type;
856 13         36 $type = 'undef';
857             }
858             }
859 522         3789 return Template::Alloy::Exception->new($type, $info, @_);
860             }
861              
862 465     465 1 1794 sub throw { die shift->exception(@_) }
863              
864             sub context {
865 163     163 1 523 my $self = shift;
866 163         937 require Template::Alloy::Context;
867 163         1328 return Template::Alloy::Context->new({_template => $self});
868             }
869              
870             sub iterator {
871 267     267 0 492 my $self = shift;
872 267         1892 require Template::Alloy::Iterator;
873 267         2091 Template::Alloy::Iterator->new(@_);
874             }
875              
876             sub undefined_get {
877 614     614 1 1141 my ($self, $ident, $node) = @_;
878 614 50       1767 return $self->{'UNDEFINED_GET'}->($self, $ident, $node) if $self->{'UNDEFINED_GET'};
879 614         8479 return '';
880             }
881              
882             sub undefined_any {
883 801     801 1 1272 my ($self, $ident) = @_;
884 801 50       1850 return $self->{'UNDEFINED_ANY'}->($self, $ident) if $self->{'UNDEFINED_ANY'};
885 801         1493 return;
886             }
887              
888             sub strict_throw {
889 27     27 1 67 my ($self, $ident) = @_;
890 27         92 my $v = $self->tt_var_string($ident);
891 27         74 my $temp = $self->{'_template'}->{'name'};
892 27         62 my $comp = $self->{'_component'}->{'name'};
893 27 100       126 my $msg = "undefined variable: $v in $comp".($comp ne $temp ? " while processing $temp" : '');
894 27 100       132 return $self->{'STRICT_THROW'}->($self, 'var.undef', $msg, {name => $v, component => $comp, template => $temp, ident => $ident}) if $self->{'STRICT_THROW'};
895 21         94 $self->throw('var.undef', $msg);
896             }
897              
898 9   50 9 1 42 sub list_filters { shift->{'_filters'} ||= eval { require Template::Filters; $Template::Filters::FILTERS } || {} }
      33        
899              
900             sub debug_node {
901 16     16 1 30 my ($self, $node) = @_;
902 16         45 my $info = $self->node_info($node);
903 16   100     115 my $format = $self->{'_debug_format'} || $self->{'DEBUG_FORMAT'} || "\n## \$file line \$line : [% \$text %] ##\n";
904 16         185 $format =~ s{\$(file|line|text)}{$info->{$1}}g;
905 16         118 return $format;
906             }
907              
908             sub node_info {
909 102     102 0 176 my ($self, $node) = @_;
910 102         211 my $doc = $self->{'_component'};
911 102         171 my $i = $node->[1];
912 102   50     454 my $j = $node->[2] || return ''; # META can be 0
913             return {
914 102 0 33     289 file => $doc->{'name'},
915             line => 'unknown',
916             text => 'unknown',
917             } if !$doc->{'_filename'} && !$doc->{'_content'};
918 102   66     273 $doc->{'_content'} ||= $self->slurp($doc->{'_filename'});
919 102         255 my $s = substr(${ $doc->{'_content'} }, $i, $j - $i);
  102         331  
920 102         348 $s =~ s/^\s+//;
921 102         299 $s =~ s/\s+$//;
922             return {
923 102         327 file => $doc->{'name'},
924             line => $self->get_line_number_by_index($doc, $i),
925             text => $s,
926             };
927             }
928              
929             sub get_line_number_by_index {
930 3924     3924 1 8426 my ($self, $doc, $index, $include_char) = @_;
931 3924 50 33     18648 if (! $index || $index <= 0) {
932 0 0       0 return $include_char ? (1, 1) : 1;
933             }
934              
935 3924   66     14873 my $lines = $doc->{'_line_offsets'} ||= do {
936 1680   33     4680 $doc->{'_content'} ||= $self->slurp($doc->{'_filename'});
937 1680         2405 my $i = 0;
938 1680         3324 my @lines = (0);
939 1680         2101 while (1) {
940 2184         2752 $i = index(${ $doc->{'_content'} }, "\n", $i) + 1;
  2184         6670  
941 2184 100       5614 last if $i == 0;
942 504         917 push @lines, $i;
943             }
944 1680         6105 \@lines;
945             };
946              
947             ### binary search them (this is fast even on big docs)
948 3924         7099 my ($i, $j) = (0, $#$lines);
949 3924 100       10969 if ($index > $lines->[-1]) {
950 3350         4635 $i = $j;
951             } else {
952 574         1267 while (1) {
953 2104 100       4669 last if abs($i - $j) <= 1;
954 1530         2758 my $k = int(($i + $j) / 2);
955 1530 100       3462 $j = $k if $lines->[$k] >= $index;
956 1530 100       7679 $i = $k if $lines->[$k] <= $index;
957             }
958             }
959 3924 100       18920 return $include_char ? ($i + 1, $index - $lines->[$i]) : $i + 1;
960             }
961              
962             sub ast_string {
963 35248     35248 1 50014 my ($self, $var) = @_;
964              
965 35248 100       77021 return 'undef' if ! defined $var;
966 33178 100       73279 return '['.join(', ', map { $self->ast_string($_) } @$var).']' if ref $var;
  29643         59175  
967 23192 100       137676 return $var if $var =~ /^(-?[1-9]\d{0,13}|0)$/;
968              
969 13037         18997 $var =~ s/([\'\\])/\\$1/g;
970 13037         42347 return "'$var'";
971             }
972              
973             sub tt_var_string {
974 36     36 1 85 my ($self, $ident) = @_;
975 36 100       102 if (! ref $ident) {
976 3 50 33     45 return $ident if $ident eq '0' || $ident =~ /^[1-9]\d{0,12}$/;
977 0         0 $ident =~ s/\'/\\\'/g;
978 0         0 return "'$ident'";
979             }
980 33         63 my $v = '';
981 33         109 for (my $i = 0; $i < @$ident; ) {
982 51         89 $v .= $ident->[$i++];
983 51 100       133 $v .= '('.join(',',map{$self->tt_var_string($_)} @{$ident->[$i-1]}).')' if $ident->[$i++];
  3         15  
  9         30  
984 51 100       171 $v .= $ident->[$i++] if $i < @$ident;
985             }
986 33         211 return $v;
987             }
988              
989             sub item_method_eval {
990 223     223 0 552 my $self = shift;
991 223 50       336 my $text = shift; return '' if ! defined $text;
  223         664  
992 223   100     727 my $args = shift || {};
993              
994 223   100     1107 local $self->{'_eval_recurse'} = $self->{'_eval_recurse'} || 0;
995 223 100 66     1143 $self->throw('eval_recurse', "MAX_EVAL_RECURSE $Template::Alloy::MAX_EVAL_RECURSE reached")
996             if ++$self->{'_eval_recurse'} > ($self->{'MAX_EVAL_RECURSE'} || $MAX_EVAL_RECURSE);
997              
998 193         247 my %ARGS;
999 193         489 @ARGS{ map {uc} keys %$args } = values %$args;
  27         228  
1000 193         430 delete @ARGS{ grep {! $Template::Alloy::EVAL_CONFIG->{$_}} keys %ARGS };
  27         109  
1001 193 100 100     581 $self->throw("eval_strict", "Cannot disable STRICT once it is enabled") if exists $ARGS{'STRICT'} && ! $ARGS{'STRICT'};
1002              
1003 190         445 local @$self{ keys %ARGS } = values %ARGS;
1004 190         291 my $out = '';
1005 190 100       533 $self->process_simple(\$text, $self->_vars, \$out) || $self->throw($self->error);
1006 179         1016 return $out;
1007             }
1008              
1009             1;
1010              
1011             ### See the perldoc in Template/Alloy.pod