added the .. operator as a syntactic sugar for the range filter when the step is 1
authorfabien <fabien@93ef8e89-cb99-4229-a87c-7fa0fa45744b>
Sun, 13 Dec 2009 15:13:38 +0000 (15:13 +0000)
committerfabien <fabien@93ef8e89-cb99-4229-a87c-7fa0fa45744b>
Sun, 13 Dec 2009 15:13:38 +0000 (15:13 +0000)
git-svn-id: http://svn.twig-project.org/trunk@173 93ef8e89-cb99-4229-a87c-7fa0fa45744b

CHANGELOG
doc/02-Twig-for-Template-Designers.markdown
lib/Twig/ExpressionParser.php
lib/Twig/Lexer.php
test/fixtures/expressions/dotdot.test [new file with mode: 0644]
test/unit/integrationTest.php

index 3a1bb94..d146da9 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,6 @@
 * 0.9.5-DEV
 
+ * added the .. operator (as a syntactic sugar for the range filter when the step is 1)
  * added the in operator (as a syntactic sugar for the in filter)
  * added the following filters in the Core extension: in, range
  * added support for arrays (same behavior as in PHP, a mix between lists and dictionaries, arrays and hashes)
index 4560e0f..3ed7893 100644 (file)
@@ -395,11 +395,11 @@ provided in a variable called `users`:
 >A sequence can be either an array or an object implementing the `Iterator`
 >interface.
 
-If you do need to iterate over a sequence of numbers, you can use the `range`
-filter:
+If you do need to iterate over a sequence of numbers, you can use the `..`
+operator:
 
     [twig]
-    {% for i in 0|range(10) %}
+    {% for i in 0..10 %}
       * {{ i }}
     {% endfor %}
 
@@ -409,10 +409,24 @@ is never part of the generated array).
 It can be also useful with letters:
 
     [twig]
-    {% for letter in 'a'|range('z') %}
+    {% for letter in 'a'..'z' %}
+      * {{ letter }}
+    {% endfor %}
+
+The `..` operator can take any expression at both sides:
+
+    [twig]
+    {% for letter in 'a'|upper..'z'|upper %}
       * {{ letter }}
     {% endfor %}
 
+If you need a step different from 1, you can use the `range` filter instead:
+
+    [twig]
+    {% for i in 0|range(10, 2) %}
+      * {{ i }}
+    {% endfor %}
+
 Inside of a `for` loop block you can access some special variables:
 
 | Variable              | Description
@@ -751,6 +765,9 @@ two categories:
    `true`. To perform a negative test, the whole expression should be prefixed
    with `not` ({{ not 1 in [1, 2, 3] }} would return `false`).
 
+ * `..`: Creates a sequence based on the operand before and after the operator
+   (see the `for` tag for some usage examples).
+
  * `|`: Applies a filter.
 
  * `~`: Converts all operands into strings and concatenates them. `{{ "Hello "
@@ -899,6 +916,20 @@ If you do need to iterate over a sequence of numbers:
 >**TIP**
 >The `range` filter works as the native PHP `range` function.
 
+The `..` operator (see above) is a syntactic sugar for the `range` filter
+(with a step of 1):
+
+    [twig]
+    {% for i in 0|range(10) %}
+      * {{ i }}
+    {% endfor %}
+
+    {# is equivalent to #}
+
+    {% for i in 0..10 %}
+      * {{ i }}
+    {% endfor %}
+
 ### `default`
 
 The `default` filter returns the passed default value if the value is
index b411c69..57d3030 100644 (file)
@@ -337,6 +337,10 @@ class Twig_ExpressionParser
     {
       switch ($this->parser->getCurrentToken()->getValue())
       {
+        case '..':
+          $node = $this->parseRangeExpression($node);
+          break;
+
         case '.':
         case '[':
           $node = $this->parseSubscriptExpression($node);
@@ -355,6 +359,16 @@ class Twig_ExpressionParser
     return $node;
   }
 
+  public function parseRangeExpression($node)
+  {
+    $token = $this->parser->getStream()->next();
+    $lineno = $token->getLine();
+
+    $end = $this->parseExpression();
+
+    return new Twig_Node_Expression_Filter($node, array(array('range', array($end))), $lineno);
+  }
+
   public function parseSubscriptExpression($node)
   {
     $token = $this->parser->getStream()->next();
index 8fc6f78..b3ef4e5 100644 (file)
@@ -36,7 +36,7 @@ class Twig_Lexer implements Twig_LexerInterface
   const REGEX_NAME     = '/[A-Za-z_][A-Za-z0-9_]*/A';
   const REGEX_NUMBER   = '/[0-9]+(?:\.[0-9]+)?/A';
   const REGEX_STRING   = '/(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\')/Asm';
-  const REGEX_OPERATOR = '/<=? | >=? | [!=]= | [(){}.,%*\/+~|-] | \[ | \] | \? | \:/Ax';
+  const REGEX_OPERATOR = '/<=? | >=? | [!=]= | \.\. | [(){}.,%*\/+~|-] | \[ | \] | \? | \:/Ax';
 
   public function __construct(Twig_Environment $env = null, array $options = array())
   {
diff --git a/test/fixtures/expressions/dotdot.test b/test/fixtures/expressions/dotdot.test
new file mode 100644 (file)
index 0000000..b3ee396
--- /dev/null
@@ -0,0 +1,17 @@
+--TEST--
+Twig supports the .. operator
+--TEMPLATE--
+{% for i in 0..10 %}{{ i }} {% endfor %}
+
+{% for letter in 'a'..'z' %}{{ letter }} {% endfor %}
+
+{% for letter in 'a'|upper..'z'|upper %}{{ letter }} {% endfor %}
+
+{% for i in foo[0]..foo[1] %}{{ i }} {% endfor %}
+--DATA--
+return array('foo' => array(1, 10))
+--EXPECT--
+0 1 2 3 4 5 6 7 8 9 10 
+a b c d e f g h i j k l m n o p q r s t u v w x y z 
+A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 
+1 2 3 4 5 6 7 8 9 10
index 4b89faa..4a76a21 100644 (file)
@@ -33,7 +33,7 @@ class Foo
   }
 }
 
-$t = new LimeTest(54);
+$t = new LimeTest(55);
 $fixturesDir = realpath(dirname(__FILE__).'/../fixtures/');
 
 foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($fixturesDir), RecursiveIteratorIterator::LEAVES_ONLY) as $file)