File Coverage

blib/lib/Text/Hogan/Compiler.pm
Criterion Covered Total %
statement 196 228 85.9
branch 58 70 82.8
condition 37 49 75.5
subroutine 28 33 84.8
pod 5 24 20.8
total 324 404 80.2


line stmt bran cond sub pod time code
1             package Text::Hogan::Compiler;
2             $Text::Hogan::Compiler::VERSION = '2.01';
3 4     4   832592 use Text::Hogan::Template;
  4         13  
  4         122  
4              
5 4     4   48 use 5.10.0;
  4         14  
6 4     4   21 use strict;
  4         9  
  4         73  
7 4     4   17 use warnings;
  4         9  
  4         113  
8              
9 4     4   1755 use Text::Trim 'trim';
  4         2310  
  4         15102  
10              
11             my $r_is_whitespace = qr/\S/;
12             my $r_quot = qr/"/;
13             my $r_newline = qr/\n/;
14             my $r_cr = qr/\r/;
15             my $r_slash = qr/\\/;
16              
17             my $linesep = "\u{2028}";
18             my $paragraphsep = "\u{2029}";
19             my $r_linesep = qr/\Q$linesep\E/;
20             my $r_paragraphsep = qr/\Q$paragraphsep\E/;
21              
22             my %tags = (
23             '#' => 1, '^' => 2, '<' => 3, '$' => 4,
24             '/' => 5, '!' => 6, '>' => 7, '=' => 8, '_v' => 9,
25             '{' => 10, '&' => 11, '_t' => 12
26             );
27              
28             my $Template = Text::Hogan::Template->new();
29              
30             sub new {
31 130     130 1 641604 my $class = shift;
32 130         415 return bless {}, $class;
33             }
34              
35             sub scan {
36 145     145 1 285 my ($self, $text_orig, $options) = @_;
37 145         1787 my $text = [ split //, $text_orig ];
38              
39 145         488 my $len = scalar(@$text);
40 145         340 my ($IN_TEXT, $IN_TAG_TYPE, $IN_TAG) = (0, 1, 2);
41 145         229 my $state = $IN_TEXT;
42 145         218 my $tag_type = undef;
43 145         203 my $tag = undef;
44 145         226 my $buf = "";
45 145         208 my @tokens;
46 145         213 my $seen_tag = 0;
47 145         183 my $i = 0;
48 145         201 my $line_start = 0;
49 145         258 my ($otag, $ctag) = ('{{', '}}');
50              
51             my $add_buf = sub {
52 598 100   598   1284 if (length $buf > 0) {
53 337         1140 push @tokens, { 'tag' => '_t', 'text' => $buf };
54 337         634 $buf = "";
55             }
56 145         553 };
57              
58             my $line_is_whitespace = sub {
59 187     187   299 my $is_all_whitespace = 1;
60 187         484 for (my $j = $line_start; $j < @tokens; $j++) {
61             $is_all_whitespace =
62             ($tags{$tokens[$j]{'tag'}} < $tags{'_v'}) ||
63 226   100     1644 ($tokens[$j]{'tag'} eq '_t' && $tokens[$j]{'text'} !~ $r_is_whitespace);
64 226 100       584 if (!$is_all_whitespace) {
65 123         503 return 0;
66             }
67             }
68 64         195 return $is_all_whitespace;
69 145         427 };
70              
71             my $filter_line = sub {
72 301     301   537 my ($have_seen_tag, $no_new_line) = @_;
73              
74 301         627 $add_buf->();
75              
76 301 100 100     802 if ($have_seen_tag && $line_is_whitespace->()) {
    100          
77 64         161 for (my $j = $line_start, my $next; $j < @tokens; $j++) {
78 65 100       169 if ($tokens[$j]{'text'}) {
79 25 100 100     92 if (($next = $tokens[$j+1]) && $next->{'tag'} eq '>') {
80 3         10 $next->{'indent'} = "".$tokens[$j]{'text'};
81             }
82 25         96 splice(@tokens,$j,1);
83             }
84             }
85             }
86             elsif (!$no_new_line) {
87 99         251 push @tokens, { 'tag' => "\n" };
88             }
89              
90 301         483 $seen_tag = 0;
91 301         586 $line_start = scalar @tokens;
92 145         502 };
93              
94             my $change_delimiters = sub {
95 16     16   34 my ($text_orig, $index) = @_;
96 16         64 my $text = join('', @$text_orig);
97              
98 16         37 my $close = '=' . $ctag;
99 16         74 my $close_index = index($text, $close, $index);
100 16         136 my $offset = index($text, '=', $index) + 1;
101 16         65 my @delimiters = (
102             split ' ', trim(
103             # WARNING
104             #
105             # JavaScript substring and Perl substring functions differ!
106             #
107             # JavaScript's length parameter always goes from the beginning
108             # of the string, whereas Perl's goes from the offset parameter!
109             #
110             # node> "{{=<% %>=}}".substring(3, 8)
111             # '<% %>'
112             #
113             # perl> substr("{{=<% %>=}}", 3, 8);
114             # <% %>=}}
115             #
116             #
117             substr($text, $offset, $close_index - $offset)
118             )
119             );
120              
121 16         471 $otag = $delimiters[0];
122 16         30 $ctag = $delimiters[-1];
123              
124 16         42 return $close_index + length($close) - 1;
125 145         590 };
126              
127 145 100       392 if ($options->{'delimiters'}) {
128 5         19 my @delimiters = split ' ', $options->{'delimiters'};
129 5         8 $otag = $delimiters[0];
130 5         11 $ctag = $delimiters[1];
131             }
132              
133 145         370 for (my $i = 0; $i < $len; $i++) {
134 4088 100       7453 if ($state eq $IN_TEXT) {
    100          
135 1814 100       3008 if (tag_change($otag, $text, $i)) {
136 297         403 --$i;
137 297         612 $add_buf->();
138 297         621 $state = $IN_TAG_TYPE;
139             }
140             else {
141 1517 100       2470 if (char_at($text, $i) eq "\n") {
142 156         270 $filter_line->($seen_tag);
143             }
144             else {
145 1361         2094 $buf .= char_at($text, $i);
146             }
147             }
148             }
149             elsif ($state eq $IN_TAG_TYPE) {
150 297         516 $i += length($otag) - 1;
151 297   100     744 $i += 1 while($options->{'allow_whitespace_before_hashmark'} and (char_at($text, $i+1) eq ' '));
152 297         562 $tag = $tags{char_at($text,$i + 1)};
153 297 100       687 $tag_type = $tag ? char_at($text, $i + 1) : '_v';
154 297 100       603 if ($tag_type eq '=') {
155 16         57 $i = $change_delimiters->($text, $i);
156 16         30 $state = $IN_TEXT;
157             }
158             else {
159 281 100       515 if ($tag) {
160 188         270 $i++;
161             }
162 281         415 $state = $IN_TAG;
163             }
164 297         576 $seen_tag = $i;
165             }
166             else {
167 1977 100       2915 if (tag_change($ctag, $text, $i)) {
168 281 100       714 push @tokens, {
169             'tag' => $tag_type,
170             'n' => trim($buf),
171             'otag' => $otag,
172             'ctag' => $ctag,
173             'i' => (($tag_type eq '/') ? $seen_tag - length($otag) : $i + length($ctag)),
174             };
175 281         5460 $buf = "";
176 281         441 $i += length($ctag) - 1;
177 281         391 $state = $IN_TEXT;
178 281 100       790 if ($tag_type eq '{') {
179 12 50       31 if ($ctag eq '}}') {
180 12         31 $i++;
181             }
182             else {
183 0         0 clean_triple_stache($tokens[-1]);
184             }
185             }
186             }
187             else {
188 1696         2547 $buf .= char_at($text, $i);
189             }
190             }
191             }
192              
193 145         389 $filter_line->($seen_tag, 1);
194              
195 145         2365 return \@tokens;
196             }
197              
198             sub clean_triple_stache {
199 0     0 0 0 my ($token) = @_;
200              
201 0 0       0 if (substr($token->{'n'}, length($token->{'n'}) - 1) eq '}') {
202 0         0 $token->{'n'} = substr($token->{'n'}, 0, length($token->{'n'}) - 1);
203             }
204              
205 0         0 return;
206             }
207              
208             sub tag_change {
209 3791     3791 0 6028 my ($tag, $text, $index) = @_;
210              
211 3791 100       5868 if (char_at($text, $index) ne char_at($tag, 0)) {
212 3212         6504 return 0;
213             }
214              
215 579         1416 for (my $i = 1, my $l = length($tag); $i < $l; $i++) {
216 553 100       1007 if (char_at($text, $index + $i) ne char_at($tag, $i)) {
217 1         3 return 0;
218             }
219             }
220              
221 578         1156 return 1;
222             }
223              
224             my %allowed_in_super = (
225             '_t' => 1,
226             "\n" => 1,
227             '$' => 1,
228             '/' => 1,
229             );
230              
231             sub build_tree {
232 214     214 0 423 my ($tokens, $kind, $stack, $custom_tags) = @_;
233 214         309 my (@instructions, $opener, $tail, $token);
234              
235 214         329 $tail = $stack->[-1];
236              
237 214         443 while (@$tokens > 0) {
238 692         946 $token = shift @$tokens;
239              
240 692 50 66     1604 if ($tail && ($tail->{'tag'} eq '<') && !$allowed_in_super{$token->{'tag'}}) {
      33        
241 0         0 die "Illegal content in < super tag.";
242             }
243              
244 692 100 66     2338 if ($tags{$token->{'tag'}} && ($tags{$token->{'tag'}} <= $tags{'$'} || is_opener($token, $custom_tags))) {
    100 100        
    100          
245 69         125 push @$stack, $token;
246 69         190 $token->{'nodes'} = build_tree($tokens, $token->{'tag'}, $stack, $custom_tags);
247             }
248             elsif ($token->{'tag'} eq '/') {
249 69 50       153 if (!@$stack) {
250 0         0 die "Closing tag without opener: /$token->{'n'}";
251             }
252 69         104 $opener = pop @$stack;
253 69 50 33     194 if ($token->{'n'} ne $opener->{'n'} && !is_closer($token->{'n'}, $opener->{'n'}, $custom_tags)) {
254 0         0 die "Nesting error: $opener->{'n'} vs $token->{'n'}";
255             }
256 69         152 $opener->{'end'} = $token->{'i'};
257 69         255 return \@instructions;
258             }
259             elsif ($token->{'tag'} eq "\n") {
260 99   66     347 $token->{'last'} = (@$tokens == 0) || ($tokens->[0]{'tag'} eq "\n");
261             }
262              
263 623         2100 push @instructions, $token;
264             }
265              
266 145 50       304 if (@$stack) {
267 0         0 die "Missing closing tag: ", pop(@$stack)->{'n'};
268             }
269              
270 145         453 return \@instructions;
271             }
272              
273             sub is_opener {
274 524     524 0 856 my ($token, $tags) = @_;
275              
276 524         1101 for (my $i = 0, my $l = scalar(@$tags); $i < $l; $i++) {
277 0 0       0 if ($tags->[$i]{'o'} eq $token->{'n'}) {
278 0         0 $token->{'tag'} = '#';
279 0         0 return 1;
280             }
281             }
282              
283 524         2030 return 0;
284             }
285              
286             sub is_closer {
287 0     0 0 0 my ($close, $open, $tags) = @_;
288              
289 0         0 for (my $i = 0, my $l = scalar(@$tags); $i < $l; $i++) {
290 0 0 0     0 if (($tags->[$i]{'c'} eq $close) && ($tags->[$i]{'o'} eq $open)) {
291 0         0 return 1;
292             }
293             }
294              
295 0         0 return 0;
296             }
297              
298             sub stringify_substitutions {
299 0     0 0 0 my $obj = shift;
300              
301 0         0 my @items;
302 0         0 for my $key (sort keys %$obj) {
303 0         0 push @items, sprintf('"%s" => sub { my ($self,$c,$p,$t,$i) = @_; %s }', esc($key), $obj->{$key});
304             }
305              
306 0         0 return sprintf("{ %s }", join(', ', @items));
307             }
308              
309             sub stringify_partials {
310 0     0 0 0 my $code_obj = shift;
311              
312 0         0 my @partials;
313 0         0 for my $key (sort keys %{ $code_obj->{'partials'} }) {
  0         0  
314             push @partials, sprintf('"%s" => { "name" => "%s", %s }', $key,
315             esc($code_obj->{'partials'}{$key}{'name'}),
316 0         0 stringify_partials($code_obj->{'partials'}{$key})
317             );
318             }
319              
320             return sprintf('"partials" => { %s }, "subs" => %s',
321             join(',', @partials),
322 0         0 stringify_substitutions($code_obj->{'subs'})
323             );
324             }
325              
326             sub stringify {
327 0     0 0 0 my ($self,$code_obj, $text, $options) = @_;
328             return sprintf('{ code => sub { my ($t,$c,$p,$i) = @_; %s }, %s }',
329 0         0 wrap_main($code_obj->{'code'}),
330             stringify_partials($code_obj)
331             );
332             }
333              
334             my $serial_no = 0;
335             sub generate {
336 145     145 1 300 my ($self, $tree, $text, $options) = @_;
337              
338 145         214 $serial_no = 0;
339              
340 145         457 my $context = { 'code' => "", 'subs' => {}, 'partials' => {} };
341 145         343 walk($tree, $context);
342              
343 145 50       320 if ($options->{'as_string'}) {
344 0         0 return $self->stringify($context, $text, $options);
345             }
346              
347 145         407 return $self->make_template($context, $text, $options);
348             }
349              
350             sub wrap_main {
351 145     145 0 270 my ($code) = @_;
352 145         26194 return sprintf('$t->b($i = $i || ""); %s return $t->fl();', $code);
353             }
354              
355             sub make_template {
356 145     145 0 240 my $self = shift;
357 145         272 my ($code_obj, $text, $options) = @_;
358              
359 145         281 my $template = make_partials($code_obj);
360 145         308 $template->{'code'} = eval sprintf("sub { my (\$t, \$c, \$p, \$i) = \@_; %s }", wrap_main($code_obj->{'code'}));
361 145         769 return $Template->new($template, $text, $self, $options);
362             }
363              
364             sub make_partials {
365 160     160 0 296 my ($code_obj) = @_;
366              
367 160         212 my $key;
368             my $template = {
369             'subs' => {},
370             'partials' => $code_obj->{'partials'},
371 160         549 'name' => $code_obj->{'name'},
372             };
373              
374 160         265 for my $key (sort keys %{ $template->{'partials'} }) {
  160         584  
375 15         50 $template->{'partials'}{$key} = make_partials($template->{'partials'}{$key});
376             }
377              
378 160         236 for my $key (sort keys %{ $code_obj->{'subs'} }) {
  160         414  
379 0         0 $template->{'subs'}{$key} = eval "sub { my (\$t, \$c, \$p, \$t, \$i) = \@_; $code_obj->{subs}{$key}; }";
380             }
381              
382 160         381 return $template;
383             }
384              
385             sub esc {
386 511     511 0 778 my $s = shift;
387              
388             # standard from hogan.js
389 511         1928 $s =~ s/$r_slash/\\\\/g;
390 511         1562 $s =~ s/$r_quot/\\\"/g;
391 511         1419 $s =~ s/$r_newline/\\n/g;
392 511         1406 $s =~ s/$r_cr/\\r/g;
393 511         1245 $s =~ s/$r_linesep/\\u2028/g;
394 511         1177 $s =~ s/$r_paragraphsep/\\u2029/g;
395              
396             # specific for Text::Hogan / Perl
397 511         782 $s =~ s/\$/\\\$/g;
398 511         671 $s =~ s/\@/\\@/g;
399 511         648 $s =~ s/\%/\\%/g;
400              
401 511         2100 return $s;
402             }
403              
404             sub char_at {
405 13767     13767 0 20904 my ($text, $index) = @_;
406 13767 100       22230 if (ref($text) eq 'ARRAY') {
407 9423         21719 return $text->[$index];
408             }
409             else {
410 4344         10900 return substr($text, $index, 1);
411             }
412             }
413              
414             sub choose_method {
415 184     184 0 336 my ($s) = @_;
416 184 100       622 return $s =~ m/[.]/ ? "d" : "f";
417             }
418              
419             sub create_partial {
420 15     15 0 38 my ($node, $context) = @_;
421              
422 15   50     114 my $prefix = "<" . ($context->{'prefix'} || "");
423 15         50 my $sym = $prefix . $node->{'n'} . $serial_no++;
424             $context->{'partials'}{$sym} = {
425 15         64 'name' => $node->{'n'},
426             'partials' => {},
427             };
428             $context->{'code'} .= sprintf('$t->b($t->rp("%s",$c,$p,"%s"));',
429             esc($sym),
430 15   100     39 ($node->{'indent'} || "")
431             );
432              
433 15         45 return $sym;
434             }
435              
436             my %codegen = (
437             '#' => sub {
438             my ($node, $context) = @_;
439             $context->{'code'} .= sprintf('if($t->s($t->%s("%s",$c,$p,1),$c,$p,0,%s,%s,"%s %s")) { $t->rs($c,$p,sub { my ($c,$p,$t) = @_;',
440             choose_method($node->{'n'}),
441             esc($node->{'n'}),
442             @{$node}{qw/ i end otag ctag /},
443             );
444             walk($node->{'nodes'}, $context);
445             $context->{'code'} .= '}); pop @$c;}';
446             },
447             '^' => sub {
448             my ($node, $context) = @_;
449             $context->{'code'} .= sprintf('if (!$t->s($t->%s("%s",$c,$p,1),$c,$p,1,0,0,"")){',
450             choose_method($node->{'n'}),
451             esc($node->{'n'})
452             );
453             walk($node->{'nodes'}, $context);
454             $context->{'code'} .= "};";
455             },
456             '>' => \&create_partial,
457             '<' => sub {
458             my ($node, $context) = @_;
459             my $ctx = { 'partials' => {}, 'code' => "", 'subs' => {}, 'in_partial' => 1 };
460             walk($node->{'nodes'}, $ctx);
461             my $template = $context->{'partials'}{create_partial($node, $context)};
462             $template->{'subs'} = $ctx->{'subs'};
463             $template->{'partials'} = $ctx->{'partials'};
464             },
465             '$' => sub {
466             my ($node, $context) = @_;
467             my $ctx = { 'subs' => {}, 'code' => "", 'partials' => $context->{'partials'}, 'prefix' => $node->{'n'} };
468             walk($node->{'nodes'}, $ctx);
469             $context->{'subs'}{$node->{'n'}} = $ctx->{'code'};
470             if (!$context->{'in_partial'}) {
471             $context->{'code'} += sprintf('$t->sub("%s",$c,$p,$i);',
472             esc($node->{'n'})
473             );
474             }
475             },
476             "\n" => sub {
477             my ($node, $context) = @_;
478             $context->{'code'} .= twrite(sprintf('"\n"%s', ($node->{'last'} ? "" : ' . $i')));
479             },
480             '_v' => sub {
481             my ($node, $context) = @_;
482             $context->{'code'} .= twrite(sprintf('$t->v($t->%s("%s",$c,$p,0))',
483             choose_method($node->{'n'}),
484             esc($node->{'n'})
485             ));
486             },
487             '_t' => sub {
488             my ($node, $context) = @_;
489             $context->{'code'} .= twrite(sprintf('"%s"', esc($node->{'text'})));
490             },
491             '{' => \&triple_stache,
492             '&' => \&triple_stache,
493             );
494              
495             sub triple_stache {
496 22     22 0 44 my ($node, $context) = @_;
497             $context->{'code'} .= sprintf('$t->b($t->t($t->%s("%s",$c,$p,0)));',
498             choose_method($node->{'n'}),
499 22         50 esc($node->{'n'})
500             );
501             }
502              
503 504     504 0 2134 sub twrite { sprintf '$t->b(%s);', @_ }
504              
505             sub walk {
506 214     214 0 363 my ($nodelist, $context) = @_;
507              
508 214         405 for my $node (@$nodelist) {
509 623 100       1562 my $func = $codegen{$node->{'tag'}} or next;
510 610         995 $func->($node, $context);
511             }
512              
513 214         468 return $context;
514             }
515              
516             sub parse {
517 145     145 1 352 my ($self, $tokens, $text, $options) = @_;
518 145   100     324 $options ||= {};
519 145   50     663 return build_tree($tokens, "", [], $options->{'section_tags'} || []);
520             }
521              
522             my %cache;
523              
524             sub cache_key {
525 159     159 0 303 my ($text, $options) = @_;
526 159   100     1192 return join('||', $text, !!$options->{'as_string'}, !!$options->{'numeric_string_as_string'}, !!$options->{'disable_lambda'}, ($options->{'delimiters'} || ""), ($options->{'allow_whitespace_before_hashmark'} || 0));
      100        
527             }
528              
529             sub compile {
530 159     159 1 4600 my ($self, $text, $options) = @_;
531 159   100     767 $options ||= {};
532              
533 159   100     369 $text //= "";
534              
535 159         342 my $key = cache_key($text, $options);
536 159         567 my $template = $cache{$key};
537              
538 159 100       340 if ($template) {
539 15         32 my $partials = $template->{'partials'};
540 15         29 for my $name (sort keys %{ $template->{'partials'} }) {
  15         62  
541 2         22 delete $partials->{$name}{'instance'};
542             }
543 15         69 return $template;
544             }
545              
546 144         409 $template = $self->generate(
547             $self->parse(
548             $self->scan($text, $options), $text, $options
549             ), $text, $options
550             );
551              
552 144         1436 return $cache{$key} = $template;
553             }
554              
555             1;
556              
557             __END__
558              
559             =head1 NAME
560              
561             Text::Hogan::Compiler - parse templates and output Perl code
562              
563             =head1 VERSION
564              
565             version 2.01
566              
567             =head1 SYNOPSIS
568              
569             use Text::Hogan::Compiler;
570              
571             my $compiler = Text::Hogan::Compiler->new;
572              
573             my $text = "Hello, {{name}}!";
574              
575             my $tokens = $compiler->scan($text);
576             my $tree = $compiler->parse($tokens, $text);
577             my $template = $compiler->generate($tree, $text);
578              
579             say $template->render({ name => "Alex" });
580              
581             =head1 METHODS
582              
583             =head2 new
584              
585             Takes nothing, returns a Compiler object.
586              
587             my $compiler = Text::Hogan::Compiler->new;
588              
589             =cut
590              
591             =head2 scan
592              
593             Takes template text and returns an arrayref which is a list of tokens.
594              
595             my $tokens = $compiler->scan("Hello, {{name}}!");
596              
597             Optionally takes a hashref with options. 'delimiters' is a string which
598             represents different delimiters, split by white-space. You should never
599             need to pass this directly, it it used to implement the in-template
600             delimiter-switching functionality.
601              
602             # equivalent to the above call with mustaches
603             my $tokens = Text::Hogan::Compiler->new->scan("Hello, <% name %>!", { delimiters => "<% %>" });
604              
605             'allow_whitespace_before_hashmark' is a boolean. If true,tags are allowed
606             to have space(s) between the delimiters and the opening sigil ('#', '/', '^', '<', etc.).
607              
608             my $tokens = Text::Hogan::Compiler->new->scan("Hello{{ # foo }}, again{{ / foo }}.", { allow_whitespace_before_hashmark => 1 });
609              
610             =head2 parse
611              
612             Takes the tokens returned by scan, along with the original text, and returns a
613             tree structure ready to be turned into Perl code.
614              
615             my $tree = $compiler->parse($tokens, $text);
616              
617             Optionally takes a hashref that can have a key called "section_tags" which
618             should be an arrayref. I don't know what it does. Probably something internal
619             related to recursive calls that you don't need to worry about.
620              
621             Note that a lot of error checking on your input gets done in this method, and
622             it is pretty much the only place exceptions might be thrown. Exceptions which
623             may be thrown include: "Closing tag without opener", "Missing closing tag",
624             "Nesting error" and "Illegal content in < super tag".
625              
626             =head2 generate
627              
628             Takes the parsed tree and the original text and returns a Text::Hogan::Template
629             object that you can call render on.
630              
631             my $template = $compiler->generate($tree, $text);
632              
633             Optionally takes a hashref that can have
634              
635             - a key "as_string". If that is passed then instead of getting a template object
636             back you get some stringified Perl code that you can cache somewhere on
637             disk as part of your build process.
638              
639             my $perl_code_as_string = $compiler->generate($tree, $text, { 'as_string' => 1 });
640              
641             - a key "numeric_string_as_string". If that is passed output that looks like a number
642             is NOT converted into a number (ie "01234" is NOT converted to "1234")
643              
644             my $perl_code_as_string = $compiler->generate($tree, $text, { 'numeric_string_as_string' => 1 });
645              
646              
647             The options hashref can have other keys which will be passed to
648             Text::Hogan::Template::new among other places.
649              
650             =head2 compile
651              
652             Takes a template string and calls scan, parse and generate on it and returns
653             you the Text::Hogan::Template object.
654              
655             my $template = $compiler->compile("Hello, {{name}}!");
656              
657             Also caches templates by a sensible cache key, which can be useful if you're
658             not stringifying and storing on disk or in memory anyway.
659              
660             Optionally takes a hashref that will be passed on to scan, parse, and generate.
661              
662             my $perl_code_as_string = $compiler->compile(
663             $text,
664             {
665             delimiters => "<% %>",
666             as_string => 1,
667             allow_whitespace_before_hashmark => 1,
668             },
669             );
670              
671             =head1 ENCODING
672              
673             As long as you are consistent with your use of encoding in your template
674             variables and your context variables, everything should just work. You can use
675             byte strings or character strings and you'll get what you expect.
676              
677             The only danger would be if you use byte strings of a multi-byte encoding and
678             you happen to get a clash with your delimiters, eg. if your 4 byte kanji
679             character happens to contain the ASCII '<' and '%' characters next to each
680             other. I have no idea what the likelihood of that is, but hopefully if you're
681             working with non-ASCII character sets you're also using Perl's unicode
682             character strings features.
683              
684             Compiling long character string inputs with Text::Hogan used to be extremely
685             slow but an optimisation added in version 2.00 has made the overhead much more
686             manageable.
687              
688             =head1 AUTHORS
689              
690             Started out statement-for-statement copied from hogan.js by Twitter!
691              
692             Initial translation by Alex Balhatchet (alex@balhatchet.net)
693              
694             Further improvements from:
695              
696             Ed Freyfogle
697             Mohammad S Anwar
698             Ricky Morse
699             Tom Hukins
700             Tony Finch
701             Yanick Champoux
702              
703             =cut