Merge branch '1.x' into 2.x

* 1.x:
  added the key to the map and filter filters
  added support for iterators
  removed fn in front of arrow functions
  changed arrow syntax
  added "filter", "map", and "reduce" filters
  simplified code
  fixed CS
  fixed partial output leak when a PHP fatal error occurs
This commit is contained in:
Fabien Potencier
2019-05-09 13:25:49 +02:00
25 changed files with 435 additions and 20 deletions
+69 -6
View File
@@ -15,6 +15,7 @@ namespace Twig;
use Twig\Error\SyntaxError;
use Twig\Node\Expression\AbstractExpression;
use Twig\Node\Expression\ArrayExpression;
use Twig\Node\Expression\ArrowFunctionExpression;
use Twig\Node\Expression\AssignNameExpression;
use Twig\Node\Expression\Binary\ConcatBinary;
use Twig\Node\Expression\BlockReferenceExpression;
@@ -60,8 +61,12 @@ class ExpressionParser
$this->binaryOperators = $env->getBinaryOperators();
}
public function parseExpression($precedence = 0)
public function parseExpression($precedence = 0, $allowArrow = false)
{
if ($allowArrow && $arrow = $this->parseArrow()) {
return $arrow;
}
$expr = $this->getPrimary();
$token = $this->parser->getCurrentToken();
while ($this->isBinary($token) && $this->binaryOperators[$token->getValue()]['precedence'] >= $precedence) {
@@ -90,6 +95,64 @@ class ExpressionParser
return $expr;
}
/**
* @return ArrowFunctionExpression|null
*/
private function parseArrow()
{
$stream = $this->parser->getStream();
// short array syntax (one argument, no parentheses)?
if ($stream->look(1)->test(Token::ARROW_TYPE)) {
$line = $stream->getCurrent()->getLine();
$token = $stream->expect(Token::NAME_TYPE);
$names = [new AssignNameExpression($token->getValue(), $token->getLine())];
$stream->expect(Token::ARROW_TYPE);
return new ArrowFunctionExpression($this->parseExpression(0), new Node($names), $line);
}
// first, determine if we are parsing an arrow function by finding => (long form)
$i = 0;
if (!$stream->look($i)->test(Token::PUNCTUATION_TYPE, '(')) {
return null;
}
++$i;
while (true) {
// variable name
++$i;
if (!$stream->look($i)->test(Token::PUNCTUATION_TYPE, ',')) {
break;
}
++$i;
}
if (!$stream->look($i)->test(Token::PUNCTUATION_TYPE, ')')) {
return null;
}
++$i;
if (!$stream->look($i)->test(Token::ARROW_TYPE)) {
return null;
}
// yes, let's parse it properly
$token = $stream->expect(Token::PUNCTUATION_TYPE, '(');
$line = $token->getLine();
$names = [];
while (true) {
$token = $stream->expect(Token::NAME_TYPE);
$names[] = new AssignNameExpression($token->getValue(), $token->getLine());
if (!$stream->nextIf(Token::PUNCTUATION_TYPE, ',')) {
break;
}
}
$stream->expect(Token::PUNCTUATION_TYPE, ')');
$stream->expect(Token::ARROW_TYPE);
return new ArrowFunctionExpression($this->parseExpression(0), new Node($names), $line);
}
private function getPrimary(): AbstractExpression
{
$token = $this->parser->getCurrentToken();
@@ -485,7 +548,7 @@ class ExpressionParser
if (!$this->parser->getStream()->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) {
$arguments = new Node();
} else {
$arguments = $this->parseArguments(true);
$arguments = $this->parseArguments(true, false, true);
}
$class = $this->getFilterNodeClass($name->getAttribute('value'), $token->getLine());
@@ -512,7 +575,7 @@ class ExpressionParser
*
* @throws SyntaxError
*/
public function parseArguments($namedArguments = false, $definition = false)
public function parseArguments($namedArguments = false, $definition = false, $allowArrow = false)
{
$args = [];
$stream = $this->parser->getStream();
@@ -527,7 +590,7 @@ class ExpressionParser
$token = $stream->expect(/* Token::NAME_TYPE */ 5, null, 'An argument must be a name');
$value = new NameExpression($token->getValue(), $this->parser->getCurrentToken()->getLine());
} else {
$value = $this->parseExpression();
$value = $this->parseExpression(0, $allowArrow);
}
$name = null;
@@ -544,7 +607,7 @@ class ExpressionParser
throw new SyntaxError(sprintf('A default value for an argument must be a constant (a boolean, a string, a number, or an array).'), $token->getLine(), $stream->getSourceContext());
}
} else {
$value = $this->parseExpression();
$value = $this->parseExpression(0, $allowArrow);
}
}
@@ -613,7 +676,7 @@ class ExpressionParser
$class = $this->getTestNodeClass($test);
$arguments = null;
if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) {
$arguments = $this->parser->getExpressionParser()->parseArguments(true);
$arguments = $this->parseArguments(true);
}
return new $class($node, $name, $arguments, $this->parser->getCurrentToken()->getLine());