summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2012-09-28 17:32:46 -0500
committerJesse Luehrs <doy@tozt.net>2012-09-28 17:32:46 -0500
commitc964148db9790676e892265327f567939619c349 (patch)
tree8823cc12b17e18d1c17cd2f472a6933f0c89e607 /lib
parent5d1c22c9436077c5e3dede4abe55dbc8713a1e4c (diff)
downloadtext-handlebars-c964148db9790676e892265327f567939619c349.tar.gz
text-handlebars-c964148db9790676e892265327f567939619c349.zip
get blocks working
Diffstat (limited to 'lib')
-rw-r--r--lib/Text/Handlebars.pm51
-rw-r--r--lib/Text/Handlebars/Compiler.pm38
-rw-r--r--lib/Text/Xslate/Syntax/Handlebars.pm87
3 files changed, 176 insertions, 0 deletions
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;