mirror of
https://github.com/alchemy-fr/Phraseanet.git
synced 2025-10-12 20:43:25 +00:00
539 lines
18 KiB
PHP
Executable File
539 lines
18 KiB
PHP
Executable File
<?php
|
|
|
|
/*
|
|
* This file is part of Twig.
|
|
*
|
|
* (c) 2009 Fabien Potencier
|
|
* (c) 2009 Armin Ronacher
|
|
*
|
|
* For the full copyright and license information, please view the LICENSE
|
|
* file that was distributed with this source code.
|
|
*/
|
|
|
|
/**
|
|
* Parses expressions.
|
|
*
|
|
* @package twig
|
|
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
|
*/
|
|
class Twig_ExpressionParser
|
|
{
|
|
protected $parser;
|
|
|
|
public function __construct(Twig_Parser $parser)
|
|
{
|
|
$this->parser = $parser;
|
|
}
|
|
|
|
public function parseExpression()
|
|
{
|
|
return $this->parseConditionalExpression();
|
|
}
|
|
|
|
public function parseConditionalExpression()
|
|
{
|
|
$lineno = $this->parser->getCurrentToken()->getLine();
|
|
$expr1 = $this->parseOrExpression();
|
|
while ($this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '?')) {
|
|
$this->parser->getStream()->next();
|
|
$expr2 = $this->parseOrExpression();
|
|
$this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, ':');
|
|
$expr3 = $this->parseConditionalExpression();
|
|
$expr1 = new Twig_Node_Expression_Conditional($expr1, $expr2, $expr3, $lineno);
|
|
$lineno = $this->parser->getCurrentToken()->getLine();
|
|
}
|
|
|
|
return $expr1;
|
|
}
|
|
|
|
public function parseOrExpression()
|
|
{
|
|
$lineno = $this->parser->getCurrentToken()->getLine();
|
|
$left = $this->parseAndExpression();
|
|
while ($this->parser->getStream()->test('or')) {
|
|
$this->parser->getStream()->next();
|
|
$right = $this->parseAndExpression();
|
|
$left = new Twig_Node_Expression_Binary_Or($left, $right, $lineno);
|
|
$lineno = $this->parser->getCurrentToken()->getLine();
|
|
}
|
|
|
|
return $left;
|
|
}
|
|
|
|
public function parseAndExpression()
|
|
{
|
|
$lineno = $this->parser->getCurrentToken()->getLine();
|
|
$left = $this->parseCompareExpression();
|
|
while ($this->parser->getStream()->test('and')) {
|
|
$this->parser->getStream()->next();
|
|
$right = $this->parseCompareExpression();
|
|
$left = new Twig_Node_Expression_Binary_And($left, $right, $lineno);
|
|
$lineno = $this->parser->getCurrentToken()->getLine();
|
|
}
|
|
|
|
return $left;
|
|
}
|
|
|
|
public function parseCompareExpression()
|
|
{
|
|
static $operators = array('==', '!=', '<', '>', '>=', '<=');
|
|
$lineno = $this->parser->getCurrentToken()->getLine();
|
|
$expr = $this->parseAddExpression();
|
|
$ops = array();
|
|
$negated = false;
|
|
while (
|
|
$this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, $operators)
|
|
||
|
|
($this->parser->getStream()->test(Twig_Token::NAME_TYPE, 'not') && $this->parser->getStream()->look()->test(Twig_Token::NAME_TYPE, 'in'))
|
|
||
|
|
$this->parser->getStream()->test(Twig_Token::NAME_TYPE, 'in')
|
|
) {
|
|
$this->parser->getStream()->rewind();
|
|
if ($this->parser->getStream()->test(Twig_Token::NAME_TYPE, 'not')) {
|
|
$negated = true;
|
|
$this->parser->getStream()->next();
|
|
}
|
|
$ops[] = new Twig_Node_Expression_Constant($this->parser->getStream()->next()->getValue(), $lineno);
|
|
$ops[] = $this->parseAddExpression();
|
|
}
|
|
|
|
if (empty($ops)) {
|
|
return $expr;
|
|
}
|
|
|
|
$node = new Twig_Node_Expression_Compare($expr, new Twig_Node($ops), $lineno);
|
|
|
|
if ($negated) {
|
|
$node = new Twig_Node_Expression_Unary_Not($node, $lineno);
|
|
}
|
|
|
|
return $node;
|
|
}
|
|
|
|
public function parseAddExpression()
|
|
{
|
|
$lineno = $this->parser->getCurrentToken()->getLine();
|
|
$left = $this->parseSubExpression();
|
|
while ($this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '+')) {
|
|
$this->parser->getStream()->next();
|
|
$right = $this->parseSubExpression();
|
|
$left = new Twig_Node_Expression_Binary_Add($left, $right, $lineno);
|
|
$lineno = $this->parser->getCurrentToken()->getLine();
|
|
}
|
|
|
|
return $left;
|
|
}
|
|
|
|
public function parseSubExpression()
|
|
{
|
|
$lineno = $this->parser->getCurrentToken()->getLine();
|
|
$left = $this->parseConcatExpression();
|
|
while ($this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '-')) {
|
|
$this->parser->getStream()->next();
|
|
$right = $this->parseConcatExpression();
|
|
$left = new Twig_Node_Expression_Binary_Sub($left, $right, $lineno);
|
|
$lineno = $this->parser->getCurrentToken()->getLine();
|
|
}
|
|
|
|
return $left;
|
|
}
|
|
|
|
public function parseConcatExpression()
|
|
{
|
|
$lineno = $this->parser->getCurrentToken()->getLine();
|
|
$left = $this->parseMulExpression();
|
|
while ($this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '~')) {
|
|
$this->parser->getStream()->next();
|
|
$right = $this->parseMulExpression();
|
|
$left = new Twig_Node_Expression_Binary_Concat($left, $right, $lineno);
|
|
$lineno = $this->parser->getCurrentToken()->getLine();
|
|
}
|
|
|
|
return $left;
|
|
}
|
|
|
|
public function parseMulExpression()
|
|
{
|
|
$lineno = $this->parser->getCurrentToken()->getLine();
|
|
$left = $this->parseDivExpression();
|
|
while ($this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '*')) {
|
|
$this->parser->getStream()->next();
|
|
$right = $this->parseDivExpression();
|
|
$left = new Twig_Node_Expression_Binary_Mul($left, $right, $lineno);
|
|
$lineno = $this->parser->getCurrentToken()->getLine();
|
|
}
|
|
|
|
return $left;
|
|
}
|
|
|
|
public function parseDivExpression()
|
|
{
|
|
$lineno = $this->parser->getCurrentToken()->getLine();
|
|
$left = $this->parseFloorDivExpression();
|
|
while ($this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '/')) {
|
|
$this->parser->getStream()->next();
|
|
$right = $this->parseModExpression();
|
|
$left = new Twig_Node_Expression_Binary_Div($left, $right, $lineno);
|
|
$lineno = $this->parser->getCurrentToken()->getLine();
|
|
}
|
|
|
|
return $left;
|
|
}
|
|
|
|
public function parseFloorDivExpression()
|
|
{
|
|
$lineno = $this->parser->getCurrentToken()->getLine();
|
|
$left = $this->parseModExpression();
|
|
while ($this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '//')) {
|
|
$this->parser->getStream()->next();
|
|
$right = $this->parseModExpression();
|
|
$left = new Twig_Node_Expression_Binary_FloorDiv($left, $right, $lineno);
|
|
$lineno = $this->parser->getCurrentToken()->getLine();
|
|
}
|
|
|
|
return $left;
|
|
}
|
|
|
|
public function parseModExpression()
|
|
{
|
|
$lineno = $this->parser->getCurrentToken()->getLine();
|
|
$left = $this->parseUnaryExpression();
|
|
while ($this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '%')) {
|
|
$this->parser->getStream()->next();
|
|
$right = $this->parseUnaryExpression();
|
|
$left = new Twig_Node_Expression_Binary_Mod($left, $right, $lineno);
|
|
$lineno = $this->parser->getCurrentToken()->getLine();
|
|
}
|
|
|
|
return $left;
|
|
}
|
|
|
|
public function parseUnaryExpression()
|
|
{
|
|
if ($this->parser->getStream()->test('not')) {
|
|
return $this->parseNotExpression();
|
|
}
|
|
if ($this->parser->getCurrentToken()->getType() == Twig_Token::OPERATOR_TYPE) {
|
|
switch ($this->parser->getCurrentToken()->getValue()) {
|
|
case '-':
|
|
return $this->parseNegExpression();
|
|
case '+':
|
|
return $this->parsePosExpression();
|
|
}
|
|
}
|
|
|
|
return $this->parsePrimaryExpression();
|
|
}
|
|
|
|
public function parseNotExpression()
|
|
{
|
|
$token = $this->parser->getStream()->next();
|
|
$node = $this->parseUnaryExpression();
|
|
|
|
return new Twig_Node_Expression_Unary_Not($node, $token->getLine());
|
|
}
|
|
|
|
public function parseNegExpression()
|
|
{
|
|
$token = $this->parser->getStream()->next();
|
|
$node = $this->parseUnaryExpression();
|
|
|
|
return new Twig_Node_Expression_Unary_Neg($node, $token->getLine());
|
|
}
|
|
|
|
public function parsePosExpression()
|
|
{
|
|
$token = $this->parser->getStream()->next();
|
|
$node = $this->parseUnaryExpression();
|
|
|
|
return new Twig_Node_Expression_Unary_Pos($node, $token->getLine());
|
|
}
|
|
|
|
public function parsePrimaryExpression($assignment = false)
|
|
{
|
|
$token = $this->parser->getCurrentToken();
|
|
switch ($token->getType()) {
|
|
case Twig_Token::NAME_TYPE:
|
|
$this->parser->getStream()->next();
|
|
switch ($token->getValue()) {
|
|
case 'true':
|
|
$node = new Twig_Node_Expression_Constant(true, $token->getLine());
|
|
break;
|
|
|
|
case 'false':
|
|
$node = new Twig_Node_Expression_Constant(false, $token->getLine());
|
|
break;
|
|
|
|
case 'none':
|
|
$node = new Twig_Node_Expression_Constant(null, $token->getLine());
|
|
break;
|
|
|
|
default:
|
|
$cls = $assignment ? 'Twig_Node_Expression_AssignName' : 'Twig_Node_Expression_Name';
|
|
$node = new $cls($token->getValue(), $token->getLine());
|
|
}
|
|
break;
|
|
|
|
case Twig_Token::NUMBER_TYPE:
|
|
case Twig_Token::STRING_TYPE:
|
|
$this->parser->getStream()->next();
|
|
$node = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
|
|
break;
|
|
|
|
default:
|
|
if ($token->test(Twig_Token::OPERATOR_TYPE, '[')) {
|
|
$node = $this->parseArrayExpression();
|
|
} elseif ($token->test(Twig_Token::OPERATOR_TYPE, '(')) {
|
|
$this->parser->getStream()->next();
|
|
$node = $this->parseExpression();
|
|
$this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, ')');
|
|
} else {
|
|
throw new Twig_Error_Syntax(sprintf('Unexpected token "%s" of value "%s"', Twig_Token::getTypeAsString($token->getType()), $token->getValue()), $token->getLine());
|
|
}
|
|
}
|
|
|
|
if (!$assignment) {
|
|
$node = $this->parsePostfixExpression($node);
|
|
}
|
|
|
|
return $node;
|
|
}
|
|
|
|
public function parseArrayExpression()
|
|
{
|
|
$stream = $this->parser->getStream();
|
|
$stream->expect(Twig_Token::OPERATOR_TYPE, '[');
|
|
$elements = array();
|
|
while (!$stream->test(Twig_Token::OPERATOR_TYPE, ']')) {
|
|
if (!empty($elements)) {
|
|
$stream->expect(Twig_Token::OPERATOR_TYPE, ',');
|
|
|
|
// trailing ,?
|
|
if ($stream->test(Twig_Token::OPERATOR_TYPE, ']')) {
|
|
$stream->expect(Twig_Token::OPERATOR_TYPE, ']');
|
|
|
|
return new Twig_Node_Expression_Array($elements, $this->parser->getCurrentToken()->getLine());
|
|
}
|
|
}
|
|
|
|
// hash or array element?
|
|
if (
|
|
$stream->test(Twig_Token::STRING_TYPE)
|
|
||
|
|
$stream->test(Twig_Token::NUMBER_TYPE)
|
|
)
|
|
{
|
|
if ($stream->look()->test(Twig_Token::OPERATOR_TYPE, ':')) {
|
|
// hash
|
|
$key = $stream->next()->getValue();
|
|
$stream->next();
|
|
|
|
$elements[$key] = $this->parseExpression();
|
|
|
|
continue;
|
|
}
|
|
$stream->rewind();
|
|
}
|
|
|
|
$elements[] = $this->parseExpression();
|
|
}
|
|
$stream->expect(Twig_Token::OPERATOR_TYPE, ']');
|
|
|
|
return new Twig_Node_Expression_Array($elements, $this->parser->getCurrentToken()->getLine());
|
|
}
|
|
|
|
public function parsePostfixExpression($node)
|
|
{
|
|
while (1) {
|
|
$token = $this->parser->getCurrentToken();
|
|
if ($token->getType() == Twig_Token::OPERATOR_TYPE) {
|
|
if ('..' == $token->getValue()) {
|
|
$node = $this->parseRangeExpression($node);
|
|
} elseif ('.' == $token->getValue() || '[' == $token->getValue()) {
|
|
$node = $this->parseSubscriptExpression($node);
|
|
} elseif ('|' == $token->getValue()) {
|
|
$node = $this->parseFilterExpression($node);
|
|
} else {
|
|
break;
|
|
}
|
|
} elseif ($token->getType() == Twig_Token::NAME_TYPE && 'is' == $token->getValue()) {
|
|
$node = $this->parseTestExpression($node);
|
|
break;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $node;
|
|
}
|
|
|
|
public function parseTestExpression($node)
|
|
{
|
|
$stream = $this->parser->getStream();
|
|
$token = $stream->next();
|
|
$lineno = $token->getLine();
|
|
|
|
$negated = false;
|
|
if ($stream->test('not')) {
|
|
$stream->next();
|
|
$negated = true;
|
|
}
|
|
|
|
$name = $stream->expect(Twig_Token::NAME_TYPE);
|
|
|
|
$arguments = null;
|
|
if ($stream->test(Twig_Token::OPERATOR_TYPE, '(')) {
|
|
$arguments = $this->parseArguments($node);
|
|
}
|
|
$test = new Twig_Node_Expression_Test($node, $name->getValue(), $arguments, $lineno);
|
|
|
|
if ($negated) {
|
|
$test = new Twig_Node_Expression_Unary_Not($test, $lineno);
|
|
}
|
|
|
|
return $test;
|
|
}
|
|
|
|
public function parseRangeExpression($node)
|
|
{
|
|
$token = $this->parser->getStream()->next();
|
|
$lineno = $token->getLine();
|
|
|
|
$end = $this->parseExpression();
|
|
|
|
$name = new Twig_Node_Expression_Constant('range', $lineno);
|
|
$arguments = new Twig_Node(array($end));
|
|
|
|
return new Twig_Node_Expression_Filter($node, $name, $arguments, $lineno);
|
|
}
|
|
|
|
public function parseSubscriptExpression($node)
|
|
{
|
|
$token = $this->parser->getStream()->next();
|
|
$lineno = $token->getLine();
|
|
$arguments = new Twig_Node();
|
|
$type = Twig_Node_Expression_GetAttr::TYPE_ANY;
|
|
if ($token->getValue() == '.') {
|
|
$token = $this->parser->getStream()->next();
|
|
if ($token->getType() == Twig_Token::NAME_TYPE || $token->getType() == Twig_Token::NUMBER_TYPE) {
|
|
$arg = new Twig_Node_Expression_Constant($token->getValue(), $lineno);
|
|
|
|
if ($this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '(')) {
|
|
$type = Twig_Node_Expression_GetAttr::TYPE_METHOD;
|
|
$arguments = $this->parseArguments();
|
|
} else {
|
|
$arguments = new Twig_Node();
|
|
}
|
|
} else {
|
|
throw new Twig_Error_Syntax('Expected name or number', $lineno);
|
|
}
|
|
} else {
|
|
$type = Twig_Node_Expression_GetAttr::TYPE_ARRAY;
|
|
|
|
$arg = $this->parseExpression();
|
|
$this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, ']');
|
|
}
|
|
|
|
return new Twig_Node_Expression_GetAttr($node, $arg, $arguments, $type, $lineno);
|
|
}
|
|
|
|
public function parseFilterExpression($node)
|
|
{
|
|
$this->parser->getStream()->next();
|
|
|
|
return $this->parseFilterExpressionRaw($node);
|
|
}
|
|
|
|
public function parseFilterExpressionRaw($node, $tag = null)
|
|
{
|
|
$lineno = $this->parser->getCurrentToken()->getLine();
|
|
|
|
while (true) {
|
|
$token = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE);
|
|
|
|
$name = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
|
|
if (!$this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '(')) {
|
|
$arguments = new Twig_Node();
|
|
} else {
|
|
$arguments = $this->parseArguments();
|
|
}
|
|
|
|
$node = new Twig_Node_Expression_Filter($node, $name, $arguments, $token->getLine(), $tag);
|
|
|
|
if (!$this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '|')) {
|
|
break;
|
|
}
|
|
|
|
$this->parser->getStream()->next();
|
|
}
|
|
|
|
return $node;
|
|
}
|
|
|
|
public function parseArguments()
|
|
{
|
|
$parser = $this->parser->getStream();
|
|
$parser->expect(Twig_Token::OPERATOR_TYPE, '(');
|
|
|
|
$args = array();
|
|
while (!$parser->test(Twig_Token::OPERATOR_TYPE, ')')) {
|
|
if (!empty($args)) {
|
|
$parser->expect(Twig_Token::OPERATOR_TYPE, ',');
|
|
}
|
|
$args[] = $this->parseExpression();
|
|
}
|
|
$parser->expect(Twig_Token::OPERATOR_TYPE, ')');
|
|
|
|
return new Twig_Node($args);
|
|
}
|
|
|
|
public function parseAssignmentExpression()
|
|
{
|
|
$lineno = $this->parser->getCurrentToken()->getLine();
|
|
$targets = array();
|
|
while (true) {
|
|
if (!empty($targets)) {
|
|
$this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, ',');
|
|
}
|
|
if ($this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, ')') ||
|
|
$this->parser->getStream()->test(Twig_Token::VAR_END_TYPE) ||
|
|
$this->parser->getStream()->test(Twig_Token::BLOCK_END_TYPE) ||
|
|
$this->parser->getStream()->test('in'))
|
|
{
|
|
break;
|
|
}
|
|
$targets[] = $this->parsePrimaryExpression(true);
|
|
if (!$this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, ',')) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return new Twig_Node($targets);
|
|
}
|
|
|
|
public function parseMultitargetExpression()
|
|
{
|
|
$lineno = $this->parser->getCurrentToken()->getLine();
|
|
$targets = array();
|
|
$is_multitarget = false;
|
|
while (true) {
|
|
if (!empty($targets)) {
|
|
$this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, ',');
|
|
}
|
|
if ($this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, ')') ||
|
|
$this->parser->getStream()->test(Twig_Token::VAR_END_TYPE) ||
|
|
$this->parser->getStream()->test(Twig_Token::BLOCK_END_TYPE))
|
|
{
|
|
break;
|
|
}
|
|
$targets[] = $this->parseExpression();
|
|
if (!$this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, ',')) {
|
|
break;
|
|
}
|
|
$is_multitarget = true;
|
|
}
|
|
|
|
return array($is_multitarget, new Twig_Node($targets));
|
|
}
|
|
}
|