2011-10-05 06:22:53 +02:00
< ? 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 .
*
* This parser implements a " Precedence climbing " algorithm .
*
* @ see http :// www . engr . mun . ca /~ theo / Misc / exp_parsing . htm
* @ see http :// en . wikipedia . org / wiki / Operator - precedence_parser
*
2013-08-01 21:20:12 +02:00
* @ author Fabien Potencier < fabien @ symfony . com >
2011-10-05 06:22:53 +02:00
*/
class Twig_ExpressionParser
{
const OPERATOR_LEFT = 1 ;
const OPERATOR_RIGHT = 2 ;
protected $parser ;
protected $unaryOperators ;
protected $binaryOperators ;
public function __construct ( Twig_Parser $parser , array $unaryOperators , array $binaryOperators )
{
$this -> parser = $parser ;
$this -> unaryOperators = $unaryOperators ;
$this -> binaryOperators = $binaryOperators ;
}
public function parseExpression ( $precedence = 0 )
{
$expr = $this -> getPrimary ();
$token = $this -> parser -> getCurrentToken ();
while ( $this -> isBinary ( $token ) && $this -> binaryOperators [ $token -> getValue ()][ 'precedence' ] >= $precedence ) {
$op = $this -> binaryOperators [ $token -> getValue ()];
$this -> parser -> getStream () -> next ();
if ( isset ( $op [ 'callable' ])) {
$expr = call_user_func ( $op [ 'callable' ], $this -> parser , $expr );
} else {
$expr1 = $this -> parseExpression ( self :: OPERATOR_LEFT === $op [ 'associativity' ] ? $op [ 'precedence' ] + 1 : $op [ 'precedence' ]);
$class = $op [ 'class' ];
$expr = new $class ( $expr , $expr1 , $token -> getLine ());
}
$token = $this -> parser -> getCurrentToken ();
}
if ( 0 === $precedence ) {
return $this -> parseConditionalExpression ( $expr );
}
return $expr ;
}
protected function getPrimary ()
{
$token = $this -> parser -> getCurrentToken ();
if ( $this -> isUnary ( $token )) {
$operator = $this -> unaryOperators [ $token -> getValue ()];
$this -> parser -> getStream () -> next ();
$expr = $this -> parseExpression ( $operator [ 'precedence' ]);
$class = $operator [ 'class' ];
return $this -> parsePostfixExpression ( new $class ( $expr , $token -> getLine ()));
} elseif ( $token -> test ( Twig_Token :: PUNCTUATION_TYPE , '(' )) {
$this -> parser -> getStream () -> next ();
$expr = $this -> parseExpression ();
$this -> parser -> getStream () -> expect ( Twig_Token :: PUNCTUATION_TYPE , ')' , 'An opened parenthesis is not properly closed' );
return $this -> parsePostfixExpression ( $expr );
}
return $this -> parsePrimaryExpression ();
}
protected function parseConditionalExpression ( $expr )
{
2015-03-11 00:16:45 +01:00
while ( $this -> parser -> getStream () -> nextIf ( Twig_Token :: PUNCTUATION_TYPE , '?' )) {
if ( ! $this -> parser -> getStream () -> nextIf ( Twig_Token :: PUNCTUATION_TYPE , ':' )) {
2013-08-01 21:20:12 +02:00
$expr2 = $this -> parseExpression ();
2015-03-11 00:16:45 +01:00
if ( $this -> parser -> getStream () -> nextIf ( Twig_Token :: PUNCTUATION_TYPE , ':' )) {
2013-08-01 21:20:12 +02:00
$expr3 = $this -> parseExpression ();
} else {
$expr3 = new Twig_Node_Expression_Constant ( '' , $this -> parser -> getCurrentToken () -> getLine ());
}
} else {
$expr2 = $expr ;
$expr3 = $this -> parseExpression ();
}
2011-10-05 06:22:53 +02:00
$expr = new Twig_Node_Expression_Conditional ( $expr , $expr2 , $expr3 , $this -> parser -> getCurrentToken () -> getLine ());
}
return $expr ;
}
protected function isUnary ( Twig_Token $token )
{
return $token -> test ( Twig_Token :: OPERATOR_TYPE ) && isset ( $this -> unaryOperators [ $token -> getValue ()]);
}
protected function isBinary ( Twig_Token $token )
{
return $token -> test ( Twig_Token :: OPERATOR_TYPE ) && isset ( $this -> binaryOperators [ $token -> getValue ()]);
}
public function parsePrimaryExpression ()
{
$token = $this -> parser -> getCurrentToken ();
switch ( $token -> getType ()) {
case Twig_Token :: NAME_TYPE :
$this -> parser -> getStream () -> next ();
switch ( $token -> getValue ()) {
case 'true' :
case 'TRUE' :
$node = new Twig_Node_Expression_Constant ( true , $token -> getLine ());
break ;
case 'false' :
case 'FALSE' :
$node = new Twig_Node_Expression_Constant ( false , $token -> getLine ());
break ;
case 'none' :
case 'NONE' :
case 'null' :
case 'NULL' :
$node = new Twig_Node_Expression_Constant ( null , $token -> getLine ());
break ;
default :
if ( '(' === $this -> parser -> getCurrentToken () -> getValue ()) {
$node = $this -> getFunctionNode ( $token -> getValue (), $token -> getLine ());
} else {
$node = new Twig_Node_Expression_Name ( $token -> getValue (), $token -> getLine ());
}
}
break ;
case Twig_Token :: NUMBER_TYPE :
$this -> parser -> getStream () -> next ();
$node = new Twig_Node_Expression_Constant ( $token -> getValue (), $token -> getLine ());
break ;
2013-08-01 21:20:12 +02:00
case Twig_Token :: STRING_TYPE :
case Twig_Token :: INTERPOLATION_START_TYPE :
$node = $this -> parseStringExpression ();
break ;
2015-03-11 00:16:45 +01:00
case Twig_Token :: OPERATOR_TYPE :
if ( preg_match ( Twig_Lexer :: REGEX_NAME , $token -> getValue (), $matches ) && $matches [ 0 ] == $token -> getValue ()) {
// in this context, string operators are variable names
$this -> parser -> getStream () -> next ();
$node = new Twig_Node_Expression_Name ( $token -> getValue (), $token -> getLine ());
break ;
} elseif ( isset ( $this -> unaryOperators [ $token -> getValue ()])) {
$class = $this -> unaryOperators [ $token -> getValue ()][ 'class' ];
$ref = new ReflectionClass ( $class );
$negClass = 'Twig_Node_Expression_Unary_Neg' ;
$posClass = 'Twig_Node_Expression_Unary_Pos' ;
if ( ! ( in_array ( $ref -> getName (), array ( $negClass , $posClass )) || $ref -> isSubclassOf ( $negClass ) || $ref -> isSubclassOf ( $posClass ))) {
throw new Twig_Error_Syntax ( sprintf ( 'Unexpected unary operator "%s"' , $token -> getValue ()), $token -> getLine (), $this -> parser -> getFilename ());
}
$this -> parser -> getStream () -> next ();
$expr = $this -> parsePrimaryExpression ();
$node = new $class ( $expr , $token -> getLine ());
break ;
}
2011-10-05 06:22:53 +02:00
default :
if ( $token -> test ( Twig_Token :: PUNCTUATION_TYPE , '[' )) {
$node = $this -> parseArrayExpression ();
} elseif ( $token -> test ( Twig_Token :: PUNCTUATION_TYPE , '{' )) {
$node = $this -> parseHashExpression ();
} else {
2015-03-11 00:16:45 +01:00
throw new Twig_Error_Syntax ( sprintf ( 'Unexpected token "%s" of value "%s"' , Twig_Token :: typeToEnglish ( $token -> getType ()), $token -> getValue ()), $token -> getLine (), $this -> parser -> getFilename ());
2011-10-05 06:22:53 +02:00
}
}
return $this -> parsePostfixExpression ( $node );
}
2013-08-01 21:20:12 +02:00
public function parseStringExpression ()
{
$stream = $this -> parser -> getStream ();
$nodes = array ();
// a string cannot be followed by another string in a single expression
$nextCanBeString = true ;
while ( true ) {
2015-03-11 00:16:45 +01:00
if ( $nextCanBeString && $token = $stream -> nextIf ( Twig_Token :: STRING_TYPE )) {
2013-08-01 21:20:12 +02:00
$nodes [] = new Twig_Node_Expression_Constant ( $token -> getValue (), $token -> getLine ());
$nextCanBeString = false ;
2015-03-11 00:16:45 +01:00
} elseif ( $stream -> nextIf ( Twig_Token :: INTERPOLATION_START_TYPE )) {
2013-08-01 21:20:12 +02:00
$nodes [] = $this -> parseExpression ();
$stream -> expect ( Twig_Token :: INTERPOLATION_END_TYPE );
$nextCanBeString = true ;
} else {
break ;
}
}
$expr = array_shift ( $nodes );
foreach ( $nodes as $node ) {
$expr = new Twig_Node_Expression_Binary_Concat ( $expr , $node , $node -> getLine ());
}
return $expr ;
}
2011-10-05 06:22:53 +02:00
public function parseArrayExpression ()
{
$stream = $this -> parser -> getStream ();
$stream -> expect ( Twig_Token :: PUNCTUATION_TYPE , '[' , 'An array element was expected' );
2013-08-01 21:20:12 +02:00
$node = new Twig_Node_Expression_Array ( array (), $stream -> getCurrent () -> getLine ());
$first = true ;
2011-10-05 06:22:53 +02:00
while ( ! $stream -> test ( Twig_Token :: PUNCTUATION_TYPE , ']' )) {
2013-08-01 21:20:12 +02:00
if ( ! $first ) {
2011-10-05 06:22:53 +02:00
$stream -> expect ( Twig_Token :: PUNCTUATION_TYPE , ',' , 'An array element must be followed by a comma' );
// trailing ,?
if ( $stream -> test ( Twig_Token :: PUNCTUATION_TYPE , ']' )) {
break ;
}
}
2013-08-01 21:20:12 +02:00
$first = false ;
2011-10-05 06:22:53 +02:00
2013-08-01 21:20:12 +02:00
$node -> addElement ( $this -> parseExpression ());
2011-10-05 06:22:53 +02:00
}
$stream -> expect ( Twig_Token :: PUNCTUATION_TYPE , ']' , 'An opened array is not properly closed' );
2013-08-01 21:20:12 +02:00
return $node ;
2011-10-05 06:22:53 +02:00
}
public function parseHashExpression ()
{
$stream = $this -> parser -> getStream ();
$stream -> expect ( Twig_Token :: PUNCTUATION_TYPE , '{' , 'A hash element was expected' );
2013-08-01 21:20:12 +02:00
$node = new Twig_Node_Expression_Array ( array (), $stream -> getCurrent () -> getLine ());
$first = true ;
2011-10-05 06:22:53 +02:00
while ( ! $stream -> test ( Twig_Token :: PUNCTUATION_TYPE , '}' )) {
2013-08-01 21:20:12 +02:00
if ( ! $first ) {
2011-10-05 06:22:53 +02:00
$stream -> expect ( Twig_Token :: PUNCTUATION_TYPE , ',' , 'A hash value must be followed by a comma' );
// trailing ,?
if ( $stream -> test ( Twig_Token :: PUNCTUATION_TYPE , '}' )) {
break ;
}
}
2013-08-01 21:20:12 +02:00
$first = false ;
// a hash key can be:
//
// * a number -- 12
// * a string -- 'a'
// * a name, which is equivalent to a string -- a
// * an expression, which must be enclosed in parentheses -- (1 + 2)
2015-03-11 00:16:45 +01:00
if (( $token = $stream -> nextIf ( Twig_Token :: STRING_TYPE )) || ( $token = $stream -> nextIf ( Twig_Token :: NAME_TYPE )) || $token = $stream -> nextIf ( Twig_Token :: NUMBER_TYPE )) {
2013-08-01 21:20:12 +02:00
$key = new Twig_Node_Expression_Constant ( $token -> getValue (), $token -> getLine ());
} elseif ( $stream -> test ( Twig_Token :: PUNCTUATION_TYPE , '(' )) {
$key = $this -> parseExpression ();
} else {
2011-10-05 06:22:53 +02:00
$current = $stream -> getCurrent ();
2013-08-01 21:20:12 +02:00
2015-03-11 00:16:45 +01:00
throw new Twig_Error_Syntax ( sprintf ( 'A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "%s" of value "%s"' , Twig_Token :: typeToEnglish ( $current -> getType ()), $current -> getValue ()), $current -> getLine (), $this -> parser -> getFilename ());
2011-10-05 06:22:53 +02:00
}
$stream -> expect ( Twig_Token :: PUNCTUATION_TYPE , ':' , 'A hash key must be followed by a colon (:)' );
2013-08-01 21:20:12 +02:00
$value = $this -> parseExpression ();
$node -> addElement ( $value , $key );
2011-10-05 06:22:53 +02:00
}
$stream -> expect ( Twig_Token :: PUNCTUATION_TYPE , '}' , 'An opened hash is not properly closed' );
2013-08-01 21:20:12 +02:00
return $node ;
2011-10-05 06:22:53 +02:00
}
public function parsePostfixExpression ( $node )
{
while ( true ) {
$token = $this -> parser -> getCurrentToken ();
if ( $token -> getType () == Twig_Token :: PUNCTUATION_TYPE ) {
if ( '.' == $token -> getValue () || '[' == $token -> getValue ()) {
$node = $this -> parseSubscriptExpression ( $node );
} elseif ( '|' == $token -> getValue ()) {
$node = $this -> parseFilterExpression ( $node );
} else {
break ;
}
} else {
break ;
}
}
return $node ;
}
public function getFunctionNode ( $name , $line )
{
switch ( $name ) {
case 'parent' :
2013-08-01 21:20:12 +02:00
$args = $this -> parseArguments ();
2011-10-05 06:22:53 +02:00
if ( ! count ( $this -> parser -> getBlockStack ())) {
2013-08-01 21:20:12 +02:00
throw new Twig_Error_Syntax ( 'Calling "parent" outside a block is forbidden' , $line , $this -> parser -> getFilename ());
2011-10-05 06:22:53 +02:00
}
2013-08-01 21:20:12 +02:00
if ( ! $this -> parser -> getParent () && ! $this -> parser -> hasTraits ()) {
throw new Twig_Error_Syntax ( 'Calling "parent" on a template that does not extend nor "use" another template is forbidden' , $line , $this -> parser -> getFilename ());
2011-10-05 06:22:53 +02:00
}
return new Twig_Node_Expression_Parent ( $this -> parser -> peekBlockStack (), $line );
case 'block' :
2013-08-01 21:20:12 +02:00
return new Twig_Node_Expression_BlockReference ( $this -> parseArguments () -> getNode ( 0 ), false , $line );
2011-10-05 06:22:53 +02:00
case 'attribute' :
2013-08-01 21:20:12 +02:00
$args = $this -> parseArguments ();
2011-10-05 06:22:53 +02:00
if ( count ( $args ) < 2 ) {
2013-08-01 21:20:12 +02:00
throw new Twig_Error_Syntax ( 'The "attribute" function takes at least two arguments (the variable and the attributes)' , $line , $this -> parser -> getFilename ());
2011-10-05 06:22:53 +02:00
}
2015-03-11 00:16:45 +01:00
return new Twig_Node_Expression_GetAttr ( $args -> getNode ( 0 ), $args -> getNode ( 1 ), count ( $args ) > 2 ? $args -> getNode ( 2 ) : null , Twig_Template :: ANY_CALL , $line );
2011-10-05 06:22:53 +02:00
default :
2015-03-11 00:16:45 +01:00
if ( null !== $alias = $this -> parser -> getImportedSymbol ( 'function' , $name )) {
$arguments = new Twig_Node_Expression_Array ( array (), $line );
foreach ( $this -> parseArguments () as $n ) {
$arguments -> addElement ( $n );
2013-09-19 08:08:25 +02:00
}
2013-08-01 21:20:12 +02:00
2015-03-11 00:16:45 +01:00
$node = new Twig_Node_Expression_MethodCall ( $alias [ 'node' ], $alias [ 'name' ], $arguments , $line );
$node -> setAttribute ( 'safe' , true );
return $node ;
2011-10-05 06:22:53 +02:00
}
2015-03-11 00:16:45 +01:00
$args = $this -> parseArguments ( true );
$class = $this -> getFunctionNodeClass ( $name , $line );
2013-08-01 21:20:12 +02:00
return new $class ( $name , $args , $line );
2011-10-05 06:22:53 +02:00
}
}
public function parseSubscriptExpression ( $node )
{
2013-08-01 21:20:12 +02:00
$stream = $this -> parser -> getStream ();
$token = $stream -> next ();
2011-10-05 06:22:53 +02:00
$lineno = $token -> getLine ();
2013-08-01 21:20:12 +02:00
$arguments = new Twig_Node_Expression_Array ( array (), $lineno );
2013-09-19 08:08:25 +02:00
$type = Twig_Template :: ANY_CALL ;
2011-10-05 06:22:53 +02:00
if ( $token -> getValue () == '.' ) {
2013-08-01 21:20:12 +02:00
$token = $stream -> next ();
2011-10-05 06:22:53 +02:00
if (
$token -> getType () == Twig_Token :: NAME_TYPE
||
$token -> getType () == Twig_Token :: NUMBER_TYPE
||
( $token -> getType () == Twig_Token :: OPERATOR_TYPE && preg_match ( Twig_Lexer :: REGEX_NAME , $token -> getValue ()))
) {
$arg = new Twig_Node_Expression_Constant ( $token -> getValue (), $lineno );
2015-03-11 00:16:45 +01:00
if ( $stream -> test ( Twig_Token :: PUNCTUATION_TYPE , '(' )) {
$type = Twig_TemplateInterface :: METHOD_CALL ;
foreach ( $this -> parseArguments () as $n ) {
$arguments -> addElement ( $n );
}
}
2011-10-05 06:22:53 +02:00
} else {
2013-08-01 21:20:12 +02:00
throw new Twig_Error_Syntax ( 'Expected name or number' , $lineno , $this -> parser -> getFilename ());
}
if ( $node instanceof Twig_Node_Expression_Name && null !== $this -> parser -> getImportedSymbol ( 'template' , $node -> getAttribute ( 'name' ))) {
if ( ! $arg instanceof Twig_Node_Expression_Constant ) {
throw new Twig_Error_Syntax ( sprintf ( 'Dynamic macro names are not supported (called on "%s")' , $node -> getAttribute ( 'name' )), $token -> getLine (), $this -> parser -> getFilename ());
}
2015-03-11 00:16:45 +01:00
$node = new Twig_Node_Expression_MethodCall ( $node , 'get' . $arg -> getAttribute ( 'value' ), $arguments , $lineno );
$node -> setAttribute ( 'safe' , true );
2013-09-19 08:08:25 +02:00
2015-03-11 00:16:45 +01:00
return $node ;
2011-10-05 06:22:53 +02:00
}
} else {
2013-09-19 08:08:25 +02:00
$type = Twig_Template :: ARRAY_CALL ;
2011-10-05 06:22:53 +02:00
2013-08-01 21:20:12 +02:00
// slice?
$slice = false ;
if ( $stream -> test ( Twig_Token :: PUNCTUATION_TYPE , ':' )) {
$slice = true ;
$arg = new Twig_Node_Expression_Constant ( 0 , $token -> getLine ());
} else {
$arg = $this -> parseExpression ();
}
2015-03-11 00:16:45 +01:00
if ( $stream -> nextIf ( Twig_Token :: PUNCTUATION_TYPE , ':' )) {
2013-08-01 21:20:12 +02:00
$slice = true ;
}
if ( $slice ) {
if ( $stream -> test ( Twig_Token :: PUNCTUATION_TYPE , ']' )) {
$length = new Twig_Node_Expression_Constant ( null , $token -> getLine ());
} else {
$length = $this -> parseExpression ();
}
$class = $this -> getFilterNodeClass ( 'slice' , $token -> getLine ());
$arguments = new Twig_Node ( array ( $arg , $length ));
$filter = new $class ( $node , new Twig_Node_Expression_Constant ( 'slice' , $token -> getLine ()), $arguments , $token -> getLine ());
$stream -> expect ( Twig_Token :: PUNCTUATION_TYPE , ']' );
return $filter ;
}
$stream -> expect ( Twig_Token :: PUNCTUATION_TYPE , ']' );
2011-10-05 06:22:53 +02:00
}
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 )
{
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 :: PUNCTUATION_TYPE , '(' )) {
$arguments = new Twig_Node ();
} else {
2013-08-01 21:20:12 +02:00
$arguments = $this -> parseArguments ( true );
2011-10-05 06:22:53 +02:00
}
2013-08-01 21:20:12 +02:00
$class = $this -> getFilterNodeClass ( $name -> getAttribute ( 'value' ), $token -> getLine ());
$node = new $class ( $node , $name , $arguments , $token -> getLine (), $tag );
2011-10-05 06:22:53 +02:00
if ( ! $this -> parser -> getStream () -> test ( Twig_Token :: PUNCTUATION_TYPE , '|' )) {
break ;
}
$this -> parser -> getStream () -> next ();
}
return $node ;
}
2013-08-01 21:20:12 +02:00
/**
* Parses arguments .
*
2015-03-11 00:16:45 +01:00
* @ param bool $namedArguments Whether to allow named arguments or not
* @ param bool $definition Whether we are parsing arguments for a function definition
2013-08-01 21:20:12 +02:00
*/
public function parseArguments ( $namedArguments = false , $definition = false )
2011-10-05 06:22:53 +02:00
{
$args = array ();
$stream = $this -> parser -> getStream ();
2013-08-01 21:20:12 +02:00
$stream -> expect ( Twig_Token :: PUNCTUATION_TYPE , '(' , 'A list of arguments must begin with an opening parenthesis' );
2011-10-05 06:22:53 +02:00
while ( ! $stream -> test ( Twig_Token :: PUNCTUATION_TYPE , ')' )) {
if ( ! empty ( $args )) {
$stream -> expect ( Twig_Token :: PUNCTUATION_TYPE , ',' , 'Arguments must be separated by a comma' );
}
2013-08-01 21:20:12 +02:00
if ( $definition ) {
$token = $stream -> expect ( Twig_Token :: NAME_TYPE , null , 'An argument must be a name' );
$value = new Twig_Node_Expression_Name ( $token -> getValue (), $this -> parser -> getCurrentToken () -> getLine ());
} else {
$value = $this -> parseExpression ();
}
$name = null ;
2015-03-11 00:16:45 +01:00
if ( $namedArguments && $token = $stream -> nextIf ( Twig_Token :: OPERATOR_TYPE , '=' )) {
2013-08-01 21:20:12 +02:00
if ( ! $value instanceof Twig_Node_Expression_Name ) {
throw new Twig_Error_Syntax ( sprintf ( 'A parameter name must be a string, "%s" given' , get_class ( $value )), $token -> getLine (), $this -> parser -> getFilename ());
}
$name = $value -> getAttribute ( 'name' );
if ( $definition ) {
$value = $this -> parsePrimaryExpression ();
if ( ! $this -> checkConstantExpression ( $value )) {
2015-03-11 00:16:45 +01:00
throw new Twig_Error_Syntax ( sprintf ( 'A default value for an argument must be a constant (a boolean, a string, a number, or an array).' ), $token -> getLine (), $this -> parser -> getFilename ());
2013-08-01 21:20:12 +02:00
}
} else {
$value = $this -> parseExpression ();
}
}
2015-03-11 00:16:45 +01:00
if ( $definition ) {
if ( null === $name ) {
$name = $value -> getAttribute ( 'name' );
$value = new Twig_Node_Expression_Constant ( null , $this -> parser -> getCurrentToken () -> getLine ());
2013-08-01 21:20:12 +02:00
}
2013-09-19 08:08:25 +02:00
$args [ $name ] = $value ;
2015-03-11 00:16:45 +01:00
} else {
if ( null === $name ) {
$args [] = $value ;
} else {
$args [ $name ] = $value ;
}
2013-08-01 21:20:12 +02:00
}
2011-10-05 06:22:53 +02:00
}
$stream -> expect ( Twig_Token :: PUNCTUATION_TYPE , ')' , 'A list of arguments must be closed by a parenthesis' );
return new Twig_Node ( $args );
}
public function parseAssignmentExpression ()
{
$targets = array ();
while ( true ) {
$token = $this -> parser -> getStream () -> expect ( Twig_Token :: NAME_TYPE , null , 'Only variables can be assigned to' );
if ( in_array ( $token -> getValue (), array ( 'true' , 'false' , 'none' ))) {
2013-08-01 21:20:12 +02:00
throw new Twig_Error_Syntax ( sprintf ( 'You cannot assign a value to "%s"' , $token -> getValue ()), $token -> getLine (), $this -> parser -> getFilename ());
2011-10-05 06:22:53 +02:00
}
$targets [] = new Twig_Node_Expression_AssignName ( $token -> getValue (), $token -> getLine ());
2015-03-11 00:16:45 +01:00
if ( ! $this -> parser -> getStream () -> nextIf ( Twig_Token :: PUNCTUATION_TYPE , ',' )) {
2011-10-05 06:22:53 +02:00
break ;
}
}
return new Twig_Node ( $targets );
}
public function parseMultitargetExpression ()
{
$targets = array ();
while ( true ) {
$targets [] = $this -> parseExpression ();
2015-03-11 00:16:45 +01:00
if ( ! $this -> parser -> getStream () -> nextIf ( Twig_Token :: PUNCTUATION_TYPE , ',' )) {
2011-10-05 06:22:53 +02:00
break ;
}
}
return new Twig_Node ( $targets );
}
2013-08-01 21:20:12 +02:00
protected function getFunctionNodeClass ( $name , $line )
{
$env = $this -> parser -> getEnvironment ();
if ( false === $function = $env -> getFunction ( $name )) {
$message = sprintf ( 'The function "%s" does not exist' , $name );
if ( $alternatives = $env -> computeAlternatives ( $name , array_keys ( $env -> getFunctions ()))) {
$message = sprintf ( '%s. Did you mean "%s"' , $message , implode ( '", "' , $alternatives ));
}
throw new Twig_Error_Syntax ( $message , $line , $this -> parser -> getFilename ());
}
if ( $function instanceof Twig_SimpleFunction ) {
return $function -> getNodeClass ();
}
return $function instanceof Twig_Function_Node ? $function -> getClass () : 'Twig_Node_Expression_Function' ;
}
protected function getFilterNodeClass ( $name , $line )
{
$env = $this -> parser -> getEnvironment ();
if ( false === $filter = $env -> getFilter ( $name )) {
$message = sprintf ( 'The filter "%s" does not exist' , $name );
if ( $alternatives = $env -> computeAlternatives ( $name , array_keys ( $env -> getFilters ()))) {
$message = sprintf ( '%s. Did you mean "%s"' , $message , implode ( '", "' , $alternatives ));
}
throw new Twig_Error_Syntax ( $message , $line , $this -> parser -> getFilename ());
}
if ( $filter instanceof Twig_SimpleFilter ) {
return $filter -> getNodeClass ();
}
return $filter instanceof Twig_Filter_Node ? $filter -> getClass () : 'Twig_Node_Expression_Filter' ;
}
// checks that the node only contains "constant" elements
protected function checkConstantExpression ( Twig_NodeInterface $node )
{
2015-03-11 00:16:45 +01:00
if ( ! ( $node instanceof Twig_Node_Expression_Constant || $node instanceof Twig_Node_Expression_Array
|| $node instanceof Twig_Node_Expression_Unary_Neg || $node instanceof Twig_Node_Expression_Unary_Pos
)) {
2013-08-01 21:20:12 +02:00
return false ;
}
foreach ( $node as $n ) {
if ( ! $this -> checkConstantExpression ( $n )) {
return false ;
}
}
return true ;
}
2011-10-05 06:22:53 +02:00
}