From 8d8d169d9e30784474856a9b43c43cf21a3d6a91 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Wed, 3 Oct 2012 16:20:14 -0500 Subject: make section lambdas work --- lib/Text/Handlebars.pm | 7 ++- lib/Text/Xslate/Syntax/Handlebars.pm | 92 +++++++++++++++++++++++++++++++----- t/mustache-spec.t | 3 -- t/mustache.t | 11 ++--- 4 files changed, 89 insertions(+), 24 deletions(-) diff --git a/lib/Text/Handlebars.pm b/lib/Text/Handlebars.pm index fa9e32c..c9a2e18 100644 --- a/lib/Text/Handlebars.pm +++ b/lib/Text/Handlebars.pm @@ -72,8 +72,11 @@ sub _register_builtin_methods { weaken(my $weakself = $self); $funcs->{'(run_code)'} = sub { - my ($code, $vars) = @_; - return $self->render_string($code->(), $vars); + my ($code, $vars, $open_tag, $close_tag, @args) = @_; + my $to_render = $code->(@args); + $to_render = "{{= $open_tag $close_tag =}}$to_render" + if defined($open_tag) && defined($close_tag) && $close_tag ne '}}'; + return $self->render_string($to_render, $vars); }; } diff --git a/lib/Text/Xslate/Syntax/Handlebars.pm b/lib/Text/Xslate/Syntax/Handlebars.pm index 70e6506..6ec5769 100644 --- a/lib/Text/Xslate/Syntax/Handlebars.pm +++ b/lib/Text/Xslate/Syntax/Handlebars.pm @@ -29,6 +29,9 @@ sub split_tags { my @chunks; + my @raw_text; + my @delimiters; + my $close_tag; my $standalone = 1; while ($input) { @@ -49,6 +52,22 @@ sub split_tags { $input =~ s/\A\Q$close_tag// or die "Oops!"; + my @extra; + if ($code =~ m{^/}) { + push @extra, pop @raw_text; + push @extra, pop @delimiters; + if (@raw_text) { + $raw_text[-1] .= $extra[0]; + } + } + if (@raw_text) { + $raw_text[-1] .= $tag_start . $code . $tag_end; + } + if ($code =~ m{^[#^]}) { + push @raw_text, ''; + push @delimiters, [$tag_start, $tag_end]; + } + my $autochomp = $code =~ m{^[!#^/=]}; if ($code =~ s/^=\s*([^\s]+)\s+([^\s]+)\s*=$//) { @@ -70,7 +89,8 @@ sub split_tags { if (length($code)) { push @chunks, [ ($close_tag eq '}}}' ? 'raw_code' : 'code'), - $code + $code, + @extra, ]; } @@ -92,12 +112,17 @@ sub split_tags { my $text = $1; if (length($text)) { push @chunks, [ text => $text ]; + if ($standalone) { $standalone = $text =~ /(?:^|\n)\s*$/; } else { $standalone = $text =~ /\n\s*$/; } + + if (@raw_text) { + $raw_text[-1] .= $text; + } } } else { @@ -126,14 +151,24 @@ sub preprocess { my $code = ''; for my $chunk (@chunks) { - my ($type, $content) = @$chunk; + my ($type, $content, $raw_text, $delimiters) = @$chunk; if ($type eq 'text') { $content =~ s/(["\\])/\\$1/g; $code .= qq{print_raw "$content";\n} if length($content); } elsif ($type eq 'code') { - $code .= qq{$content;\n}; + my $extra = ''; + if (@$chunk > 2) { + $chunk->[2] =~ s/(["\\])/\\$1/g; + $chunk->[3][0] =~ s/(["\\])/\\$1/g; + $chunk->[3][1] =~ s/(["\\])/\\$1/g; + + $extra = ' "' + . join('" "', $chunk->[2], @{ $chunk->[3] }) + . '"'; + } + $code .= qq{$content$extra;\n}; } elsif ($type eq 'raw_code') { $code .= qq{mark_raw $content;\n}; @@ -224,7 +259,11 @@ sub std_block { my $name = $self->expression(0); # variable lookups are parsed into a ternary expression, hence arity 'if' - if ($name->arity ne 'if' && $name->arity ne 'field') { + if ($name->arity eq 'if') { + $name = $name->third; + } + + if ($name->arity ne 'variable' && $name->arity ne 'field') { $self->_unexpected("opening block name", $self->token); } $self->advance(';'); @@ -232,14 +271,27 @@ sub std_block { my $body = $self->statements; $self->advance('/'); - my $closing_name = $self->expression(0); + # 1 so that i pick up names and field accesses, but not literals + # closing tags are followed by a literal string containing their raw text + my $closing_name = $self->expression(1); + if ($closing_name->arity eq 'if') { + $closing_name = $closing_name->third; + } - if ($closing_name->arity ne 'if' && $closing_name->arity ne 'field') { + if ($closing_name->arity ne 'variable' && $closing_name->arity ne 'field') { $self->_unexpected("closing block name", $self->token); } if ($closing_name->id ne $name->id) { # XXX $self->_unexpected('/' . $name->id, $self->token); } + + my $raw_text = $self->token; + $self->advance; + my $open_tag = $self->token; + $self->advance; + my $close_tag = $self->token; + $self->advance; + $self->advance(';'); my $iterations = $inverted @@ -308,11 +360,24 @@ sub std_block { ), ]; - return $self->symbol('(for)')->clone( - arity => 'for', - first => $iterations, - second => [$loop_var], - third => $body_block, + return $self->make_ternary( + $self->call('(is_code)', $name->clone), + $self->print_raw( + $self->call( + '(run_code)', + $name->clone, + $self->symbol('(vars)')->clone(arity => 'vars'), + $open_tag->clone, + $close_tag->clone, + $raw_text->clone, + ), + ), + $self->symbol('(for)')->clone( + arity => 'for', + first => $iterations, + second => [$loop_var], + third => $body_block, + ), ); } @@ -378,6 +443,11 @@ sub make_ternary { ); } +sub print_raw { + my $self = shift; + return $self->print(@_)->clone(id => 'print_raw'); +} + if (0) { require Devel::STDERR::Indent; my @stack; diff --git a/t/mustache-spec.t b/t/mustache-spec.t index 8ee1c10..a97cb10 100644 --- a/t/mustache-spec.t +++ b/t/mustache-spec.t @@ -16,9 +16,6 @@ for my $file (dir('t', 'mustache-spec', 'specs')->children) { local $TODO = "unimplemented" if $file->basename eq 'delimiters.json' && $test->{name} =~ /partial/i; - local ($TODO, $SIG{__WARN__}) = ("unimplemented", sub { }) - if $file->basename eq '~lambdas.json' - && $test->{name} =~ /section/i; render_ok( $test->{template}, diff --git a/t/mustache.t b/t/mustache.t index 6da8dd8..bbd9227 100644 --- a/t/mustache.t +++ b/t/mustache.t @@ -87,7 +87,6 @@ RENDERED "section with non-empty list" ); -{ local $TODO = "unimplemented"; local $SIG{__WARN__} = sub { }; render_ok( <<'TEMPLATE', {{#wrapped}} @@ -97,12 +96,9 @@ TEMPLATE { name => 'Willy', wrapped => sub { - return sub { - my ($text) = @_; - return '' - . Text::Handlebars->new->render_string($text) # XXX - . ''; - }; + my ($text) = @_; + chomp($text); + return "$text\n"; }, }, <<'RENDERED', @@ -110,7 +106,6 @@ TEMPLATE RENDERED "lambdas" ); -} render_ok( <<'TEMPLATE', -- cgit v1.2.3-54-g00ecf