added support for arrays (closes #50)
authorfabien <fabien@93ef8e89-cb99-4229-a87c-7fa0fa45744b>
Sun, 13 Dec 2009 10:54:01 +0000 (10:54 +0000)
committerfabien <fabien@93ef8e89-cb99-4229-a87c-7fa0fa45744b>
Sun, 13 Dec 2009 10:54:01 +0000 (10:54 +0000)
git-svn-id: http://svn.twig-project.org/trunk@162 93ef8e89-cb99-4229-a87c-7fa0fa45744b

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

index 62979f0..0299039 100644 (file)
--- 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
index 502d1eb..1e01190 100644 (file)
@@ -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' %}
index d659045..26faba2 100644 (file)
@@ -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 (file)
index 0000000..dc4970d
--- /dev/null
@@ -0,0 +1,61 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) 2009 Fabien Potencier
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+class Twig_Node_Expression_Array extends Twig_Node_Expression
+{
+  protected $elements;
+
+  public function __construct($elements, $lineno)
+  {
+    parent::__construct($lineno);
+    $this->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 (file)
index 0000000..21bff14
--- /dev/null
@@ -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,
index 8f35815..9b56a86 100644 (file)
@@ -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)