From f5b9df98be4955fa1749f645ea14183921442be2 Mon Sep 17 00:00:00 2001 From: kotas Date: Sat, 18 Jun 2011 04:00:46 +0900 Subject: [PATCH] added `if` modifier support to for loop like {% for k in v if k is odd %} --- lib/Twig/Node/For.php | 20 +++++++++++- lib/Twig/TokenParser/For.php | 8 ++++- test/Twig/Tests/Node/ForTest.php | 59 ++++++++++++++++++++++++++++++++++--- 3 files changed, 79 insertions(+), 8 deletions(-) diff --git a/lib/Twig/Node/For.php b/lib/Twig/Node/For.php index 60be15e..6c3cebc 100644 --- a/lib/Twig/Node/For.php +++ b/lib/Twig/Node/For.php @@ -18,9 +18,9 @@ */ class Twig_Node_For extends Twig_Node { - public function __construct(Twig_Node_Expression_AssignName $keyTarget, Twig_Node_Expression_AssignName $valueTarget, Twig_Node_Expression $seq, Twig_NodeInterface $body, Twig_NodeInterface $else = null, $lineno, $tag = null) + public function __construct(Twig_Node_Expression_AssignName $keyTarget, Twig_Node_Expression_AssignName $valueTarget, Twig_Node_Expression $seq, Twig_Node_Expression $ifexpr = null, Twig_NodeInterface $body, Twig_NodeInterface $else = null, $lineno, $tag = null) { - parent::__construct(array('key_target' => $keyTarget, 'value_target' => $valueTarget, 'seq' => $seq, 'body' => $body, 'else' => $else), array('with_loop' => true), $lineno, $tag); + parent::__construct(array('key_target' => $keyTarget, 'value_target' => $valueTarget, 'seq' => $seq, 'ifexpr' => $ifexpr, 'body' => $body, 'else' => $else), array('with_loop' => true), $lineno, $tag); } /** @@ -72,6 +72,15 @@ class Twig_Node_For extends Twig_Node ->indent() ; + if (null !== $this->getNode('ifexpr')) { + $compiler + ->write("if (") + ->subcompile($this->getNode('ifexpr')) + ->raw(") {\n") + ->indent() + ; + } + $compiler->subcompile($this->getNode('body')); if (null !== $this->getNode('else')) { @@ -93,6 +102,13 @@ class Twig_Node_For extends Twig_Node ; } + if (null !== $this->getNode('ifexpr')) { + $compiler + ->outdent() + ->write("}\n") + ; + } + $compiler ->outdent() ->write("}\n") diff --git a/lib/Twig/TokenParser/For.php b/lib/Twig/TokenParser/For.php index 20740d4..6041f16 100644 --- a/lib/Twig/TokenParser/For.php +++ b/lib/Twig/TokenParser/For.php @@ -25,6 +25,12 @@ class Twig_TokenParser_For extends Twig_TokenParser $this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, 'in'); $seq = $this->parser->getExpressionParser()->parseExpression(); + $ifexpr = null; + if ($this->parser->getStream()->test(Twig_Token::NAME_TYPE, 'if')) { + $this->parser->getStream()->next(); + $ifexpr = $this->parser->getExpressionParser()->parseExpression(); + } + $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE); $body = $this->parser->subparse(array($this, 'decideForFork')); if ($this->parser->getStream()->next()->getValue() == 'else') { @@ -43,7 +49,7 @@ class Twig_TokenParser_For extends Twig_TokenParser $valueTarget = $targets->getNode(0); } - return new Twig_Node_For($keyTarget, $valueTarget, $seq, $body, $else, $lineno, $this->getTag()); + return new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, $lineno, $this->getTag()); } public function decideForFork(Twig_Token $token) diff --git a/test/Twig/Tests/Node/ForTest.php b/test/Twig/Tests/Node/ForTest.php index b1a1457..18134a0 100644 --- a/test/Twig/Tests/Node/ForTest.php +++ b/test/Twig/Tests/Node/ForTest.php @@ -21,19 +21,21 @@ class Twig_Tests_Node_ForTest extends Twig_Tests_Node_TestCase $keyTarget = new Twig_Node_Expression_AssignName('key', 0); $valueTarget = new Twig_Node_Expression_AssignName('item', 0); $seq = new Twig_Node_Expression_Name('items', 0); + $ifexpr = new Twig_Node_Expression_Constant(true, 0); $body = new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 0), 0); $else = null; - $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $body, $else, 0); + $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, 0); $node->setAttribute('with_loop', false); $this->assertEquals($keyTarget, $node->getNode('key_target')); $this->assertEquals($valueTarget, $node->getNode('value_target')); $this->assertEquals($seq, $node->getNode('seq')); + $this->assertEquals($ifexpr, $node->getNode('ifexpr')); $this->assertEquals($body, $node->getNode('body')); $this->assertEquals(null, $node->getNode('else')); $else = new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 0), 0); - $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $body, $else, 0); + $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, 0); $node->setAttribute('with_loop', false); $this->assertEquals($else, $node->getNode('else')); } @@ -54,9 +56,10 @@ class Twig_Tests_Node_ForTest extends Twig_Tests_Node_TestCase $keyTarget = new Twig_Node_Expression_AssignName('key', 0); $valueTarget = new Twig_Node_Expression_AssignName('item', 0); $seq = new Twig_Node_Expression_Name('items', 0); + $ifexpr = null; $body = new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 0), 0); $else = null; - $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $body, $else, 0); + $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, 0); $node->setAttribute('with_loop', false); $tests[] = array($node, <<setAttribute('with_loop', true); $tests[] = array($node, <<setAttribute('with_loop', true); + + $tests[] = array($node, << \$context['_parent'], + 'index0' => 0, + 'index' => 1, + 'first' => true, +); +if (is_array(\$context['_seq']) || (is_object(\$context['_seq']) && \$context['_seq'] instanceof Countable)) { + \$length = count(\$context['_seq']); + \$context['loop']['revindex0'] = \$length - 1; + \$context['loop']['revindex'] = \$length; + \$context['loop']['length'] = \$length; + \$context['loop']['last'] = 1 === \$length; +} +foreach (\$context['_seq'] as \$context['k'] => \$context['v']) { + if (true) { + echo (isset(\$context['foo']) ? \$context['foo'] : null); + ++\$context['loop']['index0']; + ++\$context['loop']['index']; + \$context['loop']['first'] = false; + if (isset(\$context['loop']['length'])) { + --\$context['loop']['revindex0']; + --\$context['loop']['revindex']; + \$context['loop']['last'] = 0 === \$context['loop']['revindex0']; + } + } +} +\$_parent = \$context['_parent']; +unset(\$context['_seq'], \$context['_iterated'], \$context['k'], \$context['v'], \$context['_parent'], \$context['loop']); +\$context = array_merge(\$_parent, array_intersect_key(\$context, \$_parent)); +EOF + ); + + $keyTarget = new Twig_Node_Expression_AssignName('k', 0); + $valueTarget = new Twig_Node_Expression_AssignName('v', 0); + $seq = new Twig_Node_Expression_Name('values', 0); + $ifexpr = null; $body = new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 0), 0); $else = new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 0), 0); - $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $body, $else, 0); + $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, 0); $node->setAttribute('with_loop', true); $tests[] = array($node, <<