use constant DEBUG_GET_PARMS => 0;
-our $VERSION = "1.028";
+our $VERSION = "1.029";
my %compile_cache;
}
sub define_macro {
- my ($self, $name, $content) = @_;
+ my ($self, $name, $content, $defaults) = @_;
- $self->{defines}{$name} = $content;
+ $self->{defines}{$name} = [ $content, $defaults ];
return 1;
}
sub get_macro {
my ($self, $name) = @_;
- my $content = $self->{defines}{$name}
+ my $define = $self->{defines}{$name}
or return;
- return $content;
+ return @$define;
}
sub parse {
C<< <:.define I<name> :> I<content> <:.end:> >>
+=item *
+
+C<< <:.define I<name>; name1:value1, name2:value2 :> I<content> <:.end:> >>
+
Define a macro called I<name> with the specified content. The C<.end>
token can be C<.end define>.
some content
<:.end define:>
+The second form provides defaults for calls to the macro.
+
=item *
C<< <:.call I<name-expression> :> >>
use strict;
use Exporter qw(import);
-our $VERSION = "1.008";
+our $VERSION = "1.009";
sub _define_sequence {
my ($keys, $start) = @_;
_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);
+my @node_define = qw(NODE_DEFINE_NAME NODE_DEFINE_END NODE_DEFINE_CONTENT NODE_DEFINE_DEFAULTS);
_define_sequence(\@node_define, 4);
my @node_call = qw(NODE_CALL_NAME NODE_CALL_LIST);
_define_sequence(\@node_call, 4);
package Squirrel::Template::Expr;
use strict;
-our $VERSION = "1.012";
+our $VERSION = "1.013";
package Squirrel::Template::Expr::Eval;
use Scalar::Util ();
return [ list => $list ];
}
elsif ($t->[0] eq 'op{') {
- my @pairs;
- if ($tok->peektype eq 'op}') {
- $tok->get; # discard '}'
- }
- else {
- my $next;
- do {
- my $key = $self->_parse_additive($tok);
- my $colon = $tok->get;
- $colon->[0] eq 'op:'
- or die [ error => "Expected : in hash but found $colon->[1]" ];
- my $value = $self->_parse_expr($tok);
- push @pairs, [ $key, $value ];
- } while ($next = $tok->get and $next->[0] eq 'op,');
- $next->[0] eq 'op}'
- or die [ error => "Expected , or } but found $tok->[1]" ];
- }
+ my $pairs = $self->parse_pairs($tok);
+ my $next = $tok->get;
+ $next->[0] eq 'op}'
+ or die [ error => "Expected , or } but found $next->[1]" ];
- return [ hash => \@pairs ];
+ return [ hash => $pairs ];
}
elsif ($t->[0] eq 're') {
return [ re => $t->[2], $t->[3] ];
}
}
+sub parse_pairs {
+ my ($self, $tok) = @_;
+
+ my $nexttype = $tok->peektype;
+ if ($nexttype eq 'op}' || $nexttype eq 'eof') {
+ return [];
+ }
+ else {
+ my $next;
+ my @pairs;
+ do {
+ my $key;
+ if ($tok->peektype eq 'id') {
+ my $id = $tok->get;
+ if ($tok->peektype eq 'op:') {
+ $key = [ const => $id->[2] ];
+ }
+ else {
+ $tok->unget($id);
+ }
+ }
+ $key ||= $self->_parse_additive($tok);
+ my $colon = $tok->get;
+ $colon->[0] eq 'op:'
+ or die [ error => "Expected : in hash but found $colon->[1]" ];
+ my $value = $self->_parse_expr($tok);
+ push @pairs, [ $key, $value ];
+ } while ($next = $tok->get and $next->[0] eq 'op,');
+ $tok->unget($next);
+
+ return \@pairs;
+ }
+}
+
package Squirrel::Template::Expr::Tokenizer;
use constant TEXT => 0;
use strict;
use Squirrel::Template::Constants qw(:token :node);
-our $VERSION = "1.018";
+our $VERSION = "1.019";
use constant TOK => 0;
use constant TMPLT => 1;
my $content = $self->_parse_content;
my $end = $self->[TOK]->get;
- my $error;
+ my @errors;
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]'");
+ push @errors, $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]");
+ push @errors, $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);
+ my $text = $define->[NODE_DEFINE_NAME];
+ my $name;
+ if ($text =~ s(^([^;\s]+))()) {
+ $name = $1;
+ }
+ my $defaults;
+ my %seen_args;
+ if ($text =~ s/^\s*;\s*// && $text ne "") {
+ my $tokens = Squirrel::Template::Expr::Tokenizer->new($text);
+ my $parser = Squirrel::Template::Expr::Parser->new;
+ $defaults = $parser->parse_pairs($tokens);
+ $tokens->peektype eq 'eof'
+ or push @errors, $self->_error($end, "Defaults list for .define started $define->[TOKEN_FILENAME]:$define->[TOKEN_LINE] has extra junk");
+ }
+ $define->[NODE_DEFINE_NAME] = $name;
+
+ @{$define}[NODE_DEFINE_END, NODE_DEFINE_CONTENT, NODE_DEFINE_DEFAULTS] =
+ ( $end, $content, $defaults );
+
+ if (@errors) {
+ return $self->_comp($define, @errors);
}
else {
return $define;
my @result;
my $next = $tokens->get;
- my @args;
+ 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;
- }
+ $args = $parser->parse_pairs($tokens);
- if ($next->[0] ne 'eof') {
+ if ($tokens->peektype ne 'eof') {
die [ error => "Expected , or eof but found $next->[0]" ];
}
1;
$error = $self->_error($call, "Expected , or end of expression but found $next->[0]");
}
- @{$call}[NODE_CALL_NAME, NODE_CALL_LIST] = ( $name_expr, \@args );
+ @{$call}[NODE_CALL_NAME, NODE_CALL_LIST] = ( $name_expr, $args );
return $error ? $self->_comp($call, $error) : $call;
}
use Squirrel::Template::Constants qw(:node);
use Scalar::Util ();
-our $VERSION = "1.024";
+our $VERSION = "1.025";
use constant ACTS => 0;
use constant TMPLT => 1;
sub _process_define {
my ($self, $define) = @_;
- $self->[TMPLT]->define_macro($define->[NODE_DEFINE_NAME], $define->[NODE_DEFINE_CONTENT]);
+ $self->[TMPLT]->define_macro($define->[NODE_DEFINE_NAME], $define->[NODE_DEFINE_CONTENT], $define->[NODE_DEFINE_DEFAULTS]);
return;
}
my $parsed;
my %args;
my @result;
+ my $defaults;
my $name;
if (eval {
$name = $self->[EVAL]->process($node->[NODE_CALL_NAME]);
$args{$key} = $value;
}
- $parsed = $self->[TMPLT]->get_macro($name);
+ ($parsed, $defaults) = $self->[TMPLT]->get_macro($name);
if (!$parsed) {
($parsed, my $message) = $self->[TMPLT]->parse_file($name);
unless ($parsed) {
}) {
my $ctx = ".call '$name' from $node->[NODE_FILENAME]:$node->[NODE_LINE]";
if (eval { $self->[TMPLT]->start_scope($ctx, \%args), 1 }) {
+ if ($defaults) {
+ for my $entry (@$defaults) {
+ my ($name_expr, $value_expr)= @$entry;
+ my $name = $self->[EVAL]->process($name_expr);
+ unless (exists $args{$name}) {
+ my $value = $self->[EVAL]->process($value_expr);
+ $self->[TMPLT]->set_var($name, $value);
+ }
+ }
+ }
@result = $self->process($parsed);
$self->[TMPLT]->end_scope();
}
#!perl -w
# Basic tests for Squirrel::Template
use strict;
-use Test::More tests => 185;
+use Test::More tests => 186;
use HTML::Entities;
sub template_test($$$$;$$);
<:-.define foo:>
<:-= avar -:>
<:.end-:>
-<:.call "foo", "avar":"avalue"-:>
+<:.call "foo", avar:"avalue"-:>
<:.call "foo",
- "avar":"bvalue"-:>
+ avar:"bvalue"-:>
+IN
+ template_test(<<IN, "2716", "define defaults with call", \%acts, "both", \%vars);
+<:-.define foo; abc:1, def:abc+5 :>
+<:-= abc -:><:= def -:>
+<:.end-:>
+<:.call "foo", "abc":"2"-:>
+<:.call "foo" -:>
IN
template_test(<<IN, "other value", "external call", \%acts, "", \%vars);
<:.call "called.tmpl", "avar":"other value"-:>