From 2ab4dd5be054c83eed78df887ed2c736eb6014a9 Mon Sep 17 00:00:00 2001 From: fabien Date: Sun, 13 Dec 2009 15:13:38 +0000 Subject: [PATCH] added the .. operator as a syntactic sugar for the range filter when the step is 1 git-svn-id: http://svn.twig-project.org/trunk@173 93ef8e89-cb99-4229-a87c-7fa0fa45744b --- CHANGELOG | 1 + doc/02-Twig-for-Template-Designers.markdown | 39 ++++++++++++++++++++++++--- lib/Twig/ExpressionParser.php | 14 +++++++++ lib/Twig/Lexer.php | 2 +- test/fixtures/expressions/dotdot.test | 17 +++++++++++ test/unit/integrationTest.php | 2 +- 6 files changed, 69 insertions(+), 6 deletions(-) create mode 100644 test/fixtures/expressions/dotdot.test diff --git a/CHANGELOG b/CHANGELOG index 3a1bb94..d146da9 100644 --- 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) diff --git a/doc/02-Twig-for-Template-Designers.markdown b/doc/02-Twig-for-Template-Designers.markdown index 4560e0f..3ed7893 100644 --- a/doc/02-Twig-for-Template-Designers.markdown +++ b/doc/02-Twig-for-Template-Designers.markdown @@ -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 diff --git a/lib/Twig/ExpressionParser.php b/lib/Twig/ExpressionParser.php index b411c69..57d3030 100644 --- a/lib/Twig/ExpressionParser.php +++ b/lib/Twig/ExpressionParser.php @@ -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(); diff --git a/lib/Twig/Lexer.php b/lib/Twig/Lexer.php index 8fc6f78..b3ef4e5 100644 --- a/lib/Twig/Lexer.php +++ b/lib/Twig/Lexer.php @@ -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 index 0000000..b3ee396 --- /dev/null +++ b/test/fixtures/expressions/dotdot.test @@ -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 diff --git a/test/unit/integrationTest.php b/test/unit/integrationTest.php index 4b89faa..4a76a21 100644 --- a/test/unit/integrationTest.php +++ b/test/unit/integrationTest.php @@ -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) -- 1.7.2.5