From ee5c9d9363bd6403718c9f10e8c0d12a34083ff6 Mon Sep 17 00:00:00 2001 From: fabien Date: Sun, 13 Dec 2009 10:54:01 +0000 Subject: [PATCH] added support for arrays (closes #50) git-svn-id: http://svn.twig-project.org/trunk@162 93ef8e89-cb99-4229-a87c-7fa0fa45744b --- CHANGELOG | 3 + doc/02-Twig-for-Template-Designers.markdown | 4 ++ lib/Twig/ExpressionParser.php | 44 +++++++++++++++++++- lib/Twig/Node/Expression/Array.php | 61 +++++++++++++++++++++++++++ test/fixtures/expressions/array.test | 38 +++++++++++++++++ test/unit/integrationTest.php | 2 +- 6 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 lib/Twig/Node/Expression/Array.php create mode 100644 test/fixtures/expressions/array.test diff --git a/CHANGELOG b/CHANGELOG index 62979f0..0299039 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ * 0.9.5-DEV + * added support for arrays (same behavior as in PHP, a mix between lists and dictionaries, arrays and hashes) + * enhanced some error messages to provide better feedback in case of parsing errors + * 0.9.4 (2009-12-02) If you have custom loaders, you MUST upgrade them for this release: The diff --git a/doc/02-Twig-for-Template-Designers.markdown b/doc/02-Twig-for-Template-Designers.markdown index 502d1eb..1e01190 100644 --- a/doc/02-Twig-for-Template-Designers.markdown +++ b/doc/02-Twig-for-Template-Designers.markdown @@ -538,6 +538,10 @@ the `set` tag and can have multiple targets: [twig] {% set foo as 'foo' %} + {% set foo as [1, 2] %} + + {% set foo as ['foo': 'bar] %} + {% set foo as 'foo' ~ 'bar' %} {% set foo, bar as 'foo', 'bar' %} diff --git a/lib/Twig/ExpressionParser.php b/lib/Twig/ExpressionParser.php index d659045..26faba2 100644 --- a/lib/Twig/ExpressionParser.php +++ b/lib/Twig/ExpressionParser.php @@ -265,7 +265,13 @@ class Twig_ExpressionParser break; default: - if ($token->test(Twig_Token::OPERATOR_TYPE, '(')) + if ($token->test(Twig_Token::OPERATOR_TYPE, '[')) + { + $this->parser->getStream()->next(); + $node = $this->parseArrayExpression(); + $this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, ']'); + } + elseif ($token->test(Twig_Token::OPERATOR_TYPE, '(')) { $this->parser->getStream()->next(); $node = $this->parseExpression(); @@ -284,6 +290,42 @@ class Twig_ExpressionParser return $node; } + public function parseArrayExpression() + { + $elements = array(); + while (!$this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, ']')) + { + if (!empty($elements)) + { + $this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, ','); + } + + // hash or array element? + if ( + $this->parser->getStream()->test(Twig_Token::STRING_TYPE) + || + $this->parser->getStream()->test(Twig_Token::NUMBER_TYPE) + ) + { + if ($this->parser->getStream()->look()->test(Twig_Token::OPERATOR_TYPE, ':')) + { + // hash + $key = $this->parser->getStream()->next()->getValue(); + $this->parser->getStream()->next(); + + $elements[$key] = $this->parseExpression(); + + continue; + } + $this->parser->getStream()->rewind(); + } + + $elements[] = $this->parseExpression(); + } + + return new Twig_Node_Expression_Array($elements, $this->parser->getCurrentToken()->getLine()); + } + public function parsePostfixExpression($node) { $stop = false; diff --git a/lib/Twig/Node/Expression/Array.php b/lib/Twig/Node/Expression/Array.php new file mode 100644 index 0000000..dc4970d --- /dev/null +++ b/lib/Twig/Node/Expression/Array.php @@ -0,0 +1,61 @@ +elements = $elements; + } + + public function __toString() + { + $repr = array(get_class($this).'('); + foreach ($this->elements as $name => $node) + { + foreach (explode("\n", ' '.$name.' => '.$node) as $line) + { + $repr[] = ' '.$line; + } + } + $repr[] = ')'; + + return implode("\n", $repr); + } + + public function compile($compiler) + { + $compiler->raw('array('); + $first = true; + foreach ($this->elements as $name => $node) + { + if (!$first) + { + $compiler->raw(', '); + } + $first = false; + + $compiler + ->repr($name) + ->raw(' => ') + ->subcompile($node) + ; + } + $compiler->raw(')'); + } + + public function getElements() + { + return $this->elements; + } +} diff --git a/test/fixtures/expressions/array.test b/test/fixtures/expressions/array.test new file mode 100644 index 0000000..21bff14 --- /dev/null +++ b/test/fixtures/expressions/array.test @@ -0,0 +1,38 @@ +--TEST-- +Twig supports array notation +--TEMPLATE-- +{# empty array #} +{{ []|join(',') }} + +{{ [1, 2]|join(',') }} +{{ ['foo', "bar"]|join(',') }} +{{ [1, 'foo': 'bar']|join(',') }} +{{ [1, 'foo': 'bar']|keys|join(',') }} + +{# nested arrays #} +{% set a as [1, 2, [1, 2], 'foo': ['foo': 'bar']] %} +{{ a[2]|join(',') }} +{{ a["foo"]|join(',') }} + +{# works even if [] is used inside the array #} +{{ [foo[bar]]|join(',') }} + +{# elements can be any expression #} +{{ ['foo'|upper, bar|upper, bar == foo]|join(',') }} +--DATA-- +return array('bar' => 'bar', 'foo' => array('bar' => 'bar')) +--EXPECT-- +1,2 +foo,bar +1,bar +0,foo + + +1,2 +bar + + +bar + + +FOO,BAR, diff --git a/test/unit/integrationTest.php b/test/unit/integrationTest.php index 8f35815..9b56a86 100644 --- a/test/unit/integrationTest.php +++ b/test/unit/integrationTest.php @@ -33,7 +33,7 @@ class Foo } } -$t = new LimeTest(51); +$t = new LimeTest(52); $fixturesDir = realpath(dirname(__FILE__).'/../fixtures/'); foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($fixturesDir), RecursiveIteratorIterator::LEAVES_ONLY) as $file) -- 1.7.2.5