+Template re-re-work:
+
+ - new, faster, more regular processing internal to tags:
+ - variables supplied by code, similarly to TT, Mason
+ - macro definitions
+ - call macros or files with parameters and localized variables
+
+ TODO:
+ - documentation
+ - integration into BSE template processing
+ - preloaded template
+
BSE 0.22 - unreleased
========
t/templater/10token.t
t/templater/20parse.t
t/templater/30expr.t
+t/templates/called.tmpl
t/templates/included.include Used by t010template.t
t/templates/included.recursive
t/templates/wrapinner.tmpl
use constant DEBUG_GET_PARMS => 0;
-our $VERSION = "1.014";
+our $VERSION = "1.015";
my $tag_head = qr/(?:\s+<:-|<:-?)/;
my $tag_tail = qr/(?:-:>\s*|:>)/;
$self->{scopes}[-1]{$name} = $value;
}
+sub define_macro {
+ my ($self, $name, $content) = @_;
+
+ $self->{defines}{$name} = $content;
+
+ return 1;
+}
+
+sub get_macro {
+ my ($self, $name, $content) = @_;
+
+ my $content = $self->{defines}{$name}
+ or return;
+
+ return $content;
+}
+
sub parse {
my ($self, $template, $name) = @_;
sub replace {
my ($self, $parsed, $acts, $vars) = @_;
- $self->{errors} = [];
+ local $self->{errors} = [];
- $self->{scopes} = [];
+ local $self->{scopes} = [];
push @{$self->{scopes}}, $vars if $vars;
push @{$self->{scopes}}, { globals => {} };
+ local $self->{defines} = {};
+
my $oldparam_tag = $acts->{param};
local $acts->{param} = $oldparam_tag || [ tag_param => $self ];
use strict;
use Exporter qw(import);
-our $VERSION = "1.002";
+our $VERSION = "1.003";
sub _define_sequence {
my ($keys, $start) = @_;
_define_sequence(\@token_expr, 4);
my @token_set = qw(TOKEN_SET_VAR TOKEN_SET_EXPR);
_define_sequence(\@token_set, 4);
+my @token_end = qw(TOKEN_END_TYPE);
+_define_sequence(\@token_end, 4);
my @node_base = qw(NODE_TYPE NODE_ORIG NODE_LINE NODE_FILENAME NODE_TAG_NAME NODE_TAG_ARGS);
_define_sequence(\@node_base, 0);
_define_sequence(\@node_expr, 4);
my @node_set = qw(NODE_SET_VAR NODE_SET_EXPR);
_define_sequence(\@node_set, 4);
+my @node_define = qw(NODE_DEFINE_NAME NODE_DEFINE_END NODE_DEFINE_CONTENT);
+_define_sequence(\@node_define, 4);
+my @node_call = qw(NODE_CALL_NAME NODE_CALL_LIST);
+_define_sequence(\@node_call, 4);
our %EXPORT_TAGS =
(
- token => [ @token_base, @token_generic, @token_error, @token_expr, @token_set ],
+ token => [ @token_base, @token_generic, @token_error, @token_expr,
+ @token_set, @token_end ],
node =>
[
@node_base, @node_iter, @node_cond, @node_comp, @node_with,
- @node_wrap, @node_switch, @node_error, @node_expr, @node_set
+ @node_wrap, @node_switch, @node_error, @node_expr, @node_set,
+ @node_define, @node_call,
],
);
package Squirrel::Template::Expr;
use strict;
-our $VERSION = "1.001";
+our $VERSION = "1.002";
package Squirrel::Template::Expr::Eval;
use Scalar::Util ();
my ($self, $text) = @_;
my $tokenizer = Squirrel::Template::Expr::Tokenizer->new($text);
- return $self->_parse_expr($tokenizer);
+ my $result = $self->_parse_expr($tokenizer);
+
+ my $last = $tokenizer->get;
+ unless ($last->[0] eq 'eof') {
+ die [ error => "Expected eof but found $last->[0]" ];
+ }
+
+ return $result;
}
sub parse_tokens {
use strict;
use Squirrel::Template::Constants qw(:token :node);
-our $VERSION = "1.007";
+our $VERSION = "1.008";
use constant TOK => 0;
use constant TMPLT => 1;
elsif ($type eq 'set') {
push @result, $self->_parse_set($token);
}
+ elsif ($type eq 'call') {
+ push @result, $self->_parse_call($token);
+ }
elsif ($type eq 'if') {
push @result, $self->_parse_if($token);
}
elsif ($type eq 'error') {
push @result, $self->_parse_error($token);
}
+ elsif ($type eq 'define') {
+ push @result, $self->_parse_define($token);
+ }
elsif ($type eq 'comment') {
# discard comments
}
my $content = $self->_parse_content;
my $end = $self->[TOK]->get;
- $end or $DB::single = 1;
+
my $error;
if ($end->[TOKEN_TYPE] eq 'endwrap') {
# nothing to do
}
}
+sub _parse_define {
+ my ($self, $define) = @_;
+
+ my $content = $self->_parse_content;
+ my $end = $self->[TOK]->get;
+ my $error;
+ if ($end->[TOKEN_TYPE] eq 'end') {
+ if ($end->[TOKEN_END_TYPE] && $end->[TOKEN_END_TYPE] ne 'define') {
+ $error = $self->_error($end, "Expected '.end' or '.end define' for .define started $define->[TOKEN_FILENAME]:$define->[TOKEN_LINE] but found '.end $end->[TOKEN_END_TYPE]'");
+ }
+ }
+ else {
+ $self->[TOK]->unget($end);
+ $error = $self->_error($end, "Expected '.end' for .define started $define->[TOKEN_FILENAME]:$define->[TOKEN_LINE] but found $end->[TOKEN_TYPE]");
+ $end = $self->_empty($end);
+ }
+ @{$define}[NODE_DEFINE_END, NODE_DEFINE_CONTENT] = ( $end, $content );
+
+ if ($error) {
+ return $self->_comp($define, $error);
+ }
+ else {
+ return $define;
+ }
+}
+
+sub _parse_call {
+ my ($self, $call) = @_;
+
+ my $tokens = Squirrel::Template::Expr::Tokenizer->new($call->[TOKEN_EXPR_EXPR]);
+
+ my $error;
+ my $parser = Squirrel::Template::Expr::Parser->new;
+ my $name_expr;
+ unless (eval { $name_expr = $parser->parse_tokens($tokens); 1 }) {
+ return $self->_error($call, "Could not parse expression: ".$@->[1]);
+ }
+
+ my @result;
+ my $next = $tokens->get;
+ my @args;
+ if ($next->[0] eq 'op,') {
+ unless (eval {
+ while ($next->[0] eq 'op,') {
+ my $key;
+ my $value;
+ $key = $parser->parse_tokens($tokens);
+ my $colon = $tokens->get;
+ $colon->[0] eq 'op:'
+ or die [ error => "Expected : but found $colon->[0]" ];
+ $value = $parser->parse_tokens($tokens);
+ push @args, [ $key, $value ];
+ $next = $tokens->get;
+ }
+
+ if ($next->[0] ne 'eof') {
+ die [ error => "Expected , or eof but found $next->[0]" ];
+ }
+ 1;
+ }) {
+ return $self->_error($call, ref $@ ? $@->[0] : $@);
+ }
+ }
+ elsif ($next->[0] ne 'eof') {
+ $error = $self->_error($call, "Expected , or end of expression but found $next->[0]");
+ }
+
+ @{$call}[NODE_CALL_NAME, NODE_CALL_LIST] = ( $name_expr, \@args );
+
+ return $error ? $self->_comp($call, $error) : $call;
+}
+
sub _parse_error {
my ($self, $error) = @_;
use strict;
use Squirrel::Template::Constants qw(:node);
-our $VERSION = "1.009";
+our $VERSION = "1.010";
use constant ACTS => 0;
use constant TMPLT => 1;
return @errors;
}
+sub _process_define {
+ my ($self, $define) = @_;
+
+ $self->[TMPLT]->define_macro($define->[NODE_DEFINE_NAME], $define->[NODE_DEFINE_CONTENT]);
+
+ return;
+}
+
+sub _process_call {
+ my ($self, $node) = @_;
+
+ my $parsed;
+ my %args;
+ my @result;
+ if (eval {
+ my $name = $self->[EVAL]->process($node->[NODE_CALL_NAME]);
+ for my $arg (@{$node->[NODE_CALL_LIST]}) {
+ my $key = $self->[EVAL]->process($arg->[0]);
+ my $value = $self->[EVAL]->process($arg->[1]);
+ $args{$key} = $value;
+ }
+
+ $parsed = $self->[TMPLT]->get_macro($name);
+ if (!$parsed) {
+ ($parsed, my $message) = $self->[TMPLT]->parse_file($name);
+ $parsed
+ or die "ENOIMPL - $name not found\n";
+ }
+ 1;
+ }) {
+ $self->[TMPLT]->start_scope(\%args);
+ @result = $self->process($parsed);
+ $self->[TMPLT]->end_scope();
+ }
+ else {
+ @result = $node->[NODE_ORIG];
+ }
+
+ return @result;
+}
+
sub _process_error {
my ($self, $node) = @_;
use strict;
use Squirrel::Template::Constants qw(:token);
-our $VERSION = "1.005";
+our $VERSION = "1.006";
use constant QUEUE => 0;
use constant TEXT => 1;
elsif ($body =~ /\A\.end(?:\s+(\w+))?\z/) {
push @$queue, [ end => $tag, $line, $name, defined $1 ? $1 : "" ];
}
- elsif ($body =~ /\A\.call\s+([^,]+)(?:\s*,\s*(\S.*))?\z/) {
- push @$queue, [ call => $tag, $line, $name, $1, defined $2 ? $2 : "" ];
+ elsif ($body =~ /\A\.call\s+(\S.*)?\z/) {
+ push @$queue, [ call => $tag, $line, $name, $1 ];
}
elsif ($body =~ /\Aiterator\s+begin\s+(\w+)\s*(?:\s+(\S.*))?\z/s) {
push @$queue, [ itbegin => $tag, $line, $name, $1, defined $2 ? $2 : '' ];
#!perl -w
# Basic tests for Squirrel::Template
use strict;
-use Test::More tests => 99;
+use Test::More tests => 102;
sub template_test($$$$;$$);
template_test("<:= $expr :>", $result, "expr: $expr", \%acts, "", \%vars);
}
+
+ template_test(<<IN, "", "define no use", \%acts, "both", \%vars);
+<:-.define foo:>
+<:.end-:>
+<:-.define bar:>
+<:.end define-:>
+IN
+ template_test(<<IN, "avalue", "define with call", \%acts, "both", \%vars);
+<:-.define foo:>
+<:-= avar -:>
+<:.end-:>
+<:.call "foo", "avar":"avalue"-:>
+IN
+ template_test(<<IN, "other value", "external call", \%acts, "", \%vars);
+<:.call "called.tmpl", "avar":"other value"-:>
+IN
}
+
sub template_test ($$$$;$$) {
my ($in, $out, $desc, $acts, $stripnl, $vars) = @_;
--- /dev/null
+<:= avar -:>