From c964148db9790676e892265327f567939619c349 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Fri, 28 Sep 2012 17:32:46 -0500 Subject: get blocks working --- lib/Text/Handlebars.pm | 51 +++++++++++++++++++++ lib/Text/Handlebars/Compiler.pm | 38 ++++++++++++++++ lib/Text/Xslate/Syntax/Handlebars.pm | 87 ++++++++++++++++++++++++++++++++++++ 3 files changed, 176 insertions(+) create mode 100644 lib/Text/Handlebars.pm create mode 100644 lib/Text/Handlebars/Compiler.pm (limited to 'lib') diff --git a/lib/Text/Handlebars.pm b/lib/Text/Handlebars.pm new file mode 100644 index 0000000..bad8c64 --- /dev/null +++ b/lib/Text/Handlebars.pm @@ -0,0 +1,51 @@ +package Text::Handlebars; +use strict; +use warnings; + +use base 'Text::Xslate'; + +sub default_functions { + my $class = shift; + return { + %{ $class->SUPER::default_functions(@_) }, + '(is_array)' => sub { + my ($val) = @_; + return ref($val) && ref($val) eq 'ARRAY'; + }, + '(make_array)' => sub { + my ($length) = @_; + return [(undef) x $length]; + }, + '(new_vars_for)' => sub { + my ($vars, $value, $i) = @_; + $i = 0 unless defined $i; # XXX + + if (my $ref = ref($value)) { + if (defined $ref && $ref eq 'ARRAY') { + die "no iterator cycle provided?" + unless defined $i; + $value = $value->[$i]; + $ref = ref($value); + } + + die "invalid value: $value" + if !defined($ref) || $ref ne 'HASH'; + + return $value; + } + else { + return $vars; + } + }, + }; +} + +sub options { + my $class = shift; + + my $options = $class->SUPER::options(@_); + $options->{compiler} = 'Text::Handlebars::Compiler'; + return $options; +} + +1; diff --git a/lib/Text/Handlebars/Compiler.pm b/lib/Text/Handlebars/Compiler.pm new file mode 100644 index 0000000..2150bba --- /dev/null +++ b/lib/Text/Handlebars/Compiler.pm @@ -0,0 +1,38 @@ +package Text::Handlebars::Compiler; +use Any::Moose; + +extends 'Text::Xslate::Compiler'; + +has '+syntax' => ( + default => 'Handlebars', +); + +sub _generate_block { + my $self = shift; + my ($node) = @_; + + return ( + $self->_localize_vars($node->first), + (map { $self->compile_ast($_) } @{ $node->second }), + ); +} + +if (0) { + our $_recursing; + around compile_ast => sub { + my $orig = shift; + my $self = shift; + + my @ast = do { + local $_recursing = 1; + $self->$orig(@_); + }; + use Data::Dump; ddx(\@ast) unless $_recursing; + return @ast; + }; +} + +__PACKAGE__->meta->make_immutable; +no Any::Moose; + +1; diff --git a/lib/Text/Xslate/Syntax/Handlebars.pm b/lib/Text/Xslate/Syntax/Handlebars.pm index 3f63f68..0cfc1e0 100644 --- a/lib/Text/Xslate/Syntax/Handlebars.pm +++ b/lib/Text/Xslate/Syntax/Handlebars.pm @@ -127,8 +127,17 @@ sub init_symbols { $name->set_led($self->can('led_name')); $name->lbp(1); + my $for = $self->symbol('(for)'); + $for->arity('for'); + + my $iterator = $self->symbol('(iterator)'); + $iterator->arity('iterator'); + $self->infix('.', 256, $self->can('led_dot')); $self->infix('/', 256, $self->can('led_dot')); + + $self->symbol('#')->set_std($self->can('std_block')); + $self->prefix('/', 0)->is_block_end(1); } sub nud_name { @@ -166,6 +175,73 @@ sub led_dot { return $dot; } +sub std_block { + my $self = shift; + my ($symbol) = @_; + + if ($self->token->arity ne 'name') { + $self->_unexpected("block name", $self->token); + } + my $name = $self->token->nud($self); + $self->advance; + $self->advance(';'); + + my $body = $self->statements; + + $self->advance('/'); + + if ($self->token->arity ne 'name') { + $self->_unexpected("block name", $self->token); + } + if ($self->token->id ne $name->id) { + $self->_unexpected('/' . $name->id, $self->token); + } + + $self->advance; + + my $iterations = $self->make_ternary( + $self->call('(is_array)', $name->clone), + $name->clone, + $self->make_ternary( + $name->clone, + $self->call( + '(make_array)', + $self->symbol('(literal)')->clone(id => 1), + ), + $self->call( + '(make_array)', + $self->symbol('(literal)')->clone(id => 0), + ), + ), + ); + + my $loop_var = $self->symbol('(variable)')->clone(id => '(block)'); + + my $body_block = [ + $symbol->clone( + arity => 'block', + first => [ + $self->call( + '(new_vars_for)', + $self->symbol('(vars)')->clone(arity => 'vars'), + $name->clone, + $self->symbol('(iterator)')->clone( + id => '$~(block)', + first => $loop_var, + ), + ), + ], + second => $body, + ), + ]; + + return $self->symbol('(for)')->clone( + first => $iterations, + second => [$loop_var], + third => $body_block, + ); +} + sub make_field_lookup { my $self = shift; my ($var, $field, $dot) = @_; @@ -183,6 +259,17 @@ sub make_field_lookup { ); } +sub make_ternary { + my $self = shift; + my ($if, $then, $else) = @_; + return $self->symbol('?:')->clone( + arity => 'if', + first => $if, + second => $then, + third => $else, + ); +} + if (0) { require Devel::STDERR::Indent; my @stack; -- cgit v1.2.3-54-g00ecf