add a new .while construct to templates
authorTony Cook <tony@develop-help.com>
Fri, 22 Mar 2013 13:34:16 +0000 (00:34 +1100)
committerTony Cook <tony@develop-help.com>
Fri, 22 Mar 2013 13:34:16 +0000 (00:34 +1100)
site/cgi-bin/modules/Squirrel/Template.pm
site/cgi-bin/modules/Squirrel/Template/Constants.pm
site/cgi-bin/modules/Squirrel/Template/Parser.pm
site/cgi-bin/modules/Squirrel/Template/Processor.pm
t/020-templater/040-original.t

index 89592335464fe8ac0ecc1912d970c06d6d965e4e..8c5c25b907da17b97702bc18eb1d09ed0ffea6bc 100644 (file)
@@ -20,7 +20,7 @@ BEGIN {
 
 use constant DEBUG_GET_PARMS => 0;
 
-our $VERSION = "1.026";
+our $VERSION = "1.027";
 
 my %compile_cache;
 
@@ -863,6 +863,12 @@ C<< <:.iterateover I<callback>, I<arguments>... :> I<content> <:.end :> >>
 Calls back into the target supplied callback to set variables which
 can then be replaced on each iteration.
 
+=item *
+
+C<< <:.while I<condition> :> I<content> <:.end :> >>
+
+Produce I<content> while I<condition> is true.
+
 =back
 
 =head1 Special Variables
index 994b5597f8b826367164de20761e34bf3c4834eb..dabefe7ccc6417cbee850e1be3b623b68f85e081 100644 (file)
@@ -2,7 +2,7 @@ package Squirrel::Template::Constants;
 use strict;
 use Exporter qw(import);
 
-our $VERSION = "1.006";
+our $VERSION = "1.007";
 
 sub _define_sequence {
   my ($keys, $start) = @_;
@@ -64,6 +64,8 @@ my @node_extif = qw(NODE_EXTIF_CONDS NODE_EXTIF_ELSE NODE_EXTIF_END);
 _define_sequence(\@node_extif, 4);
 my @node_iterateover = qw(NODE_ITERATEOVER_CALL NODE_ITERATEOVER_ARGS NODE_ITERATEOVER_CONTENT);
 _define_sequence(\@node_iterateover, 4);
+my @node_while = qw(NODE_WHILE_COND NODE_WHILE_CONTENT NODE_WHILE_END);
+_define_sequence(\@node_while, 4);
 
 our %EXPORT_TAGS =
   (
@@ -74,7 +76,8 @@ our %EXPORT_TAGS =
    [
     @node_base, @node_iter, @node_cond, @node_comp, @node_with,
     @node_wrap, @node_switch, @node_error, @node_expr, @node_set,
-    @node_define, @node_call, @node_for, @node_extif, @node_iterateover
+    @node_define, @node_call, @node_for, @node_extif, @node_iterateover,
+    @node_while,
    ],
   );
 
index 7e53baa5db1530aa0d2113f4c8c5f747468ffc84..60f2894414e73c6cca4583823f2bd853f255893d 100644 (file)
@@ -2,7 +2,7 @@ package Squirrel::Template::Parser;
 use strict;
 use Squirrel::Template::Constants qw(:token :node);
 
-our $VERSION = "1.014";
+our $VERSION = "1.015";
 
 use constant TOK => 0;
 use constant TMPLT => 1;
@@ -557,6 +557,35 @@ sub _parse_ext_if {
   return @errors ? $self->_comp($if, @errors) : $if;
 }
 
+sub _parse_ext_while {
+  my ($self, $while) = @_;
+
+  my @errors;
+  my $content = $self->_parse_content;
+  my $end = $self->[TOK]->get;
+  if ($end->[TOKEN_TYPE] eq 'end') {
+    if ($end->[TOKEN_END_TYPE] ne "" && $end->[TOKEN_END_TYPE] ne 'while') {
+      push @errors, $self->_error($end, "Expected '.end' or '.end while' for .while started $while->[TOKEN_FILENAME]:$while->[TOKEN_LINE] but found '.end $end->[TOKEN_END_TYPE]'");
+    }
+  }
+  else {
+    push @errors, $self->_error($end, "Expected '.end' for .while started $while->[TOKEN_FILENAME]:$while->[TOKEN_LINE] but found $end->[TOKEN_TYPE]");
+    $self->[TOK]->unget($end);
+    $end = $self->_empty($end);
+  }
+
+  my $parser = Squirrel::Template::Expr::Parser->new;
+  my $cond_expr;
+  unless (eval { $cond_expr = $parser->parse($while->[TOKEN_EXT_EXPR]); 1 }) {
+    return $self->_error($while, "Could not parse condition for .while: " . ref $@ ? $@->[0] : $@);
+  }
+
+  @{$while}[NODE_TYPE, NODE_WHILE_COND, NODE_WHILE_CONTENT, NODE_WHILE_END] =
+    ( "while", $cond_expr, $content, $end );
+
+  return @errors ? $self->_comp($while, @errors) : $while;
+}
+
 sub _parse_iterateover {
   my ($self, $token) = @_;
 
index c8b548c0684032c8c41ad4b36abb6704e024c808..95d38c4f1161058259a6ac4a90264992df200e08 100644 (file)
@@ -3,7 +3,7 @@ use strict;
 use Squirrel::Template::Constants qw(:node);
 use Scalar::Util ();
 
-our $VERSION = "1.021";
+our $VERSION = "1.022";
 
 use constant ACTS => 0;
 use constant TMPLT => 1;
@@ -681,6 +681,53 @@ sub _process_iterateover {
   return @result;
 }
 
+sub _process_while {
+  my ($self, $node) = @_;
+
+  my $cond = $node->[NODE_WHILE_COND];
+  my $result;
+  unless (eval { $result = $self->[EVAL]->process($cond); 1 }) {
+    my $msg = $@;
+    if (!ref $msg && $msg ==~ /\bENOIMPL\b/) {
+      return
+       (
+        $node->[NODE_ORIG],
+        $self->process($node->[NODE_WHILE_CONTENT]),
+        $node->[NODE_WHILE_END][NODE_ORIG],
+       );
+    }
+    else {
+      return $self->_error($node, ref $msg ? $msg->[1] : $msg);
+    }
+  }
+  my @output;
+  while ($result) {
+    push @output, $self->process($node->[NODE_WHILE_CONTENT]);
+
+    unless (eval { $result = $self->[EVAL]->process($cond); 1 }) {
+      my $msg = $@;
+      if (!ref $msg && $msg ==~ /\bENOIMPL\b/) {
+       return
+         (
+          @output,
+          $node->[NODE_ORIG],
+          $self->process($node->[NODE_WHILE_CONTENT]),
+          $node->[NODE_WHILE_END][NODE_ORIG],
+         );
+      }
+      else {
+       return
+         (
+          @output,
+          $self->_error($node, ref $msg ? $msg->[1] : $msg),
+         );
+      }
+    }
+  }
+
+  return @output;
+}
+
 1;
 
 =head1 NAME
index ac984fe72c5b4bebb15634fffe66b69336c79dcc..f0cc910750683103cd4ccf16827c5ceb25f8f160 100644 (file)
@@ -1,7 +1,7 @@
 #!perl -w
 # Basic tests for Squirrel::Template
 use strict;
-use Test::More tests => 170;
+use Test::More tests => 180;
 use HTML::Entities;
 
 sub template_test($$$$;$$);
@@ -365,6 +365,17 @@ OUT
 IN
 <foo>FOO
 </foo>
+OUT
+
+    template_test(<<IN, <<OUT, "while", \%acts);
+<:.set work = [ "a" .. "z" ] -:>
+<:.while work.size and work[0] ne "d" -:>
+<:= work.shift :>
+<:.end while -:>
+IN
+a
+b
+c
 OUT
 
     template_test(<<IN, <<OUT, "space complex", \%acts, "both");
@@ -520,6 +531,15 @@ OUT
      [ '"test".is_hash', 0 ],
      [ '"abc".replace(/(.)(.)(.)/, "$3$2$1")', "cba" ],
      [ '"a&b".escape("html")', 'a&amp;b' ],
+     [ '"abc".match(/b/).start', "1" ],
+     [ '"abc".match(/b/).end', "2" ],
+     [ '"abc".match(/b/).length', "1" ],
+     [ '"abc".match(/(b)/).subexpr[0].start', "1" ],
+     [ '"abc".match(/(b)/).subexpr[0].end', "2" ],
+     [ '"abc".match(/(b)/).subexpr[0].length', "1" ],
+     [ '"abcd".substring(1)', "bcd" ],
+     [ '"abcd".substring(1,2)', "bc" ],
+     [ '"abcd".substring(1,-2)', "b" ],
 
      # WrapArray
      [ '[ [ 1, 2 ], 3 ].expand.join(",")', "1,2,3" ],