1818use Twig \Node \Expression \AbstractExpression ;
1919use Twig \Node \Expression \ArrayExpression ;
2020use Twig \Node \Expression \ArrowFunctionExpression ;
21- use Twig \Node \Expression \Binary \AbstractBinary ;
2221use Twig \Node \Expression \Binary \ConcatBinary ;
2322use Twig \Node \Expression \ConstantExpression ;
2423use Twig \Node \Expression \GetAttrExpression ;
3736use Twig \Node \Expression \Variable \TemplateVariable ;
3837use Twig \Node \Node ;
3938use Twig \Node \Nodes ;
39+ use Twig \Operator \Binary \AbstractBinaryOperator ;
40+ use Twig \Operator \OperatorArity ;
41+ use Twig \Operator \OperatorAssociativity ;
42+ use Twig \Operator \OperatorInterface ;
43+ use Twig \Operator \Operators ;
44+ use Twig \Operator \Unary \AbstractUnaryOperator ;
4045
4146/**
4247 * Parses expressions.
5055 */
5156class ExpressionParser
5257{
58+ // FIXME: deprecated, use OperatorAssociativity instead
5359 public const OPERATOR_LEFT = 1 ;
5460 public const OPERATOR_RIGHT = 2 ;
5561
56- /** @var array<string, array{precedence: int, precedence_change?: OperatorPrecedenceChange, class: class-string<AbstractUnary>}> */
57- private $ unaryOperators ;
58- /** @var array<string, array{precedence: int, precedence_change?: OperatorPrecedenceChange, class: class-string<AbstractBinary>, associativity: self::OPERATOR_*}> */
59- private $ binaryOperators ;
62+ private Operators $ operators ;
6063 private $ readyNodes = [];
6164 private array $ precedenceChanges = [];
6265 private bool $ deprecationCheck = true ;
@@ -65,26 +68,16 @@ public function __construct(
6568 private Parser $ parser ,
6669 private Environment $ env ,
6770 ) {
68- $ this ->unaryOperators = $ env ->getUnaryOperators ();
69- $ this ->binaryOperators = $ env ->getBinaryOperators ();
70-
71- $ ops = [];
72- foreach ($ this ->unaryOperators as $ n => $ c ) {
73- $ ops [] = $ c + ['name ' => $ n , 'type ' => 'unary ' ];
74- }
75- foreach ($ this ->binaryOperators as $ n => $ c ) {
76- $ ops [] = $ c + ['name ' => $ n , 'type ' => 'binary ' ];
77- }
78- foreach ($ ops as $ config ) {
79- if (!isset ($ config ['precedence_change ' ])) {
71+ $ this ->operators = $ env ->getOperators ();
72+ foreach ($ this ->operators as $ name => $ op ) {
73+ if (!$ op ->getPrecedenceChange ()) {
8074 continue ;
8175 }
82- $ name = $ config ['type ' ].'_ ' .$ config ['name ' ];
83- $ min = min ($ config ['precedence_change ' ]->getNewPrecedence (), $ config ['precedence ' ]);
84- $ max = max ($ config ['precedence_change ' ]->getNewPrecedence (), $ config ['precedence ' ]);
85- foreach ($ ops as $ c ) {
86- if ($ c ['precedence ' ] > $ min && $ c ['precedence ' ] < $ max ) {
87- $ this ->precedenceChanges [$ c ['type ' ].'_ ' .$ c ['name ' ]][] = $ name ;
76+ $ min = min ($ op ->getPrecedenceChange ()->getNewPrecedence (), $ op ->getPrecedence ());
77+ $ max = max ($ op ->getPrecedenceChange ()->getNewPrecedence (), $ op ->getPrecedence ());
78+ foreach ($ this ->operators as $ n => $ o ) {
79+ if ($ o ->getPrecedence () > $ min && $ o ->getPrecedence () < $ max ) {
80+ $ this ->precedenceChanges [$ n ][] = $ name ;
8881 }
8982 }
9083 }
@@ -102,28 +95,27 @@ public function parseExpression($precedence = 0)
10295
10396 $ expr = $ this ->getPrimary ();
10497 $ token = $ this ->parser ->getCurrentToken ();
105- while ($ this ->isBinary ($ token ) && $ this ->binaryOperators [$ token ->getValue ()]['precedence ' ] >= $ precedence ) {
106- $ op = $ this ->binaryOperators [$ token ->getValue ()];
98+ while ($ token ->test (Token::OPERATOR_TYPE ) && ($ op = $ this ->operators ->getBinary ($ token ->getValue ())) && $ op ->getPrecedence () >= $ precedence ) {
10799 $ this ->parser ->getStream ()->next ();
108100
109101 if ('is not ' === $ token ->getValue ()) {
110102 $ expr = $ this ->parseNotTestExpression ($ expr );
111103 } elseif ('is ' === $ token ->getValue ()) {
112104 $ expr = $ this ->parseTestExpression ($ expr );
113- } elseif (isset ( $ op[ ' callable ' ] )) {
114- $ expr = $ op[ ' callable ' ] ($ this ->parser , $ expr );
105+ } elseif (null !== $ op-> getCallable ( )) {
106+ $ expr = $ op-> getCallable () ($ this ->parser , $ expr );
115107 } else {
116108 $ previous = $ this ->setDeprecationCheck (true );
117109 try {
118- $ expr1 = $ this ->parseExpression (self :: OPERATOR_LEFT === $ op[ ' associativity ' ] ? $ op[ ' precedence ' ] + 1 : $ op[ ' precedence ' ] );
110+ $ expr1 = $ this ->parseExpression (OperatorAssociativity::Left === $ op-> getAssociativity () ? $ op-> getPrecedence () + 1 : $ op-> getPrecedence () );
119111 } finally {
120112 $ this ->setDeprecationCheck ($ previous );
121113 }
122- $ class = $ op[ ' class ' ] ;
114+ $ class = $ op-> getNodeClass () ;
123115 $ expr = new $ class ($ expr , $ expr1 , $ token ->getLine ());
124116 }
125117
126- $ expr ->setAttribute ('operator ' , ' binary_ ' .$ token ->getValue ());
118+ $ expr ->setAttribute ('operator ' , OperatorArity::Binary-> value . ' _ ' .$ token ->getValue ());
127119
128120 $ this ->triggerPrecedenceDeprecations ($ expr );
129121
@@ -156,7 +148,7 @@ private function triggerPrecedenceDeprecations(AbstractExpression $expr): void
156148 continue ;
157149 }
158150 if ($ node ->hasAttribute ('operator ' ) && $ operatorName === $ node ->getAttribute ('operator ' )) {
159- $ change = $ this ->unaryOperators [ $ target][ ' precedence_change ' ] ;
151+ $ change = $ this ->operators -> getUnary ( $ target)-> getPrecedenceChange () ;
160152 trigger_deprecation ($ change ->getPackage (), $ change ->getVersion (), \sprintf ('Add explicit parentheses around the "%s" unary operator to avoid behavior change in the next major version as its precedence will change in "%s" at line %d. ' , $ target , $ this ->parser ->getStream ()->getSourceContext ()->getName (), $ node ->getTemplateLine ()));
161153 }
162154 }
@@ -166,7 +158,7 @@ private function triggerPrecedenceDeprecations(AbstractExpression $expr): void
166158 /** @var AbstractExpression $node */
167159 if ($ node ->hasAttribute ('operator ' ) && $ operatorName === $ node ->getAttribute ('operator ' ) && !$ node ->hasExplicitParentheses ()) {
168160 $ op = explode ('_ ' , $ operatorName )[1 ];
169- $ change = $ this ->binaryOperators [ $ op][ ' precedence_change ' ] ;
161+ $ change = $ this ->operators -> getBinary ( $ op)-> getPrecedenceChange () ;
170162 trigger_deprecation ($ change ->getPackage (), $ change ->getVersion (), \sprintf ('Add explicit parentheses around the "%s" binary operator to avoid behavior change in the next major version as its precedence will change in "%s" at line %d. ' , $ op , $ this ->parser ->getStream ()->getSourceContext ()->getName (), $ node ->getTemplateLine ()));
171163 }
172164 }
@@ -236,14 +228,13 @@ private function getPrimary(): AbstractExpression
236228 {
237229 $ token = $ this ->parser ->getCurrentToken ();
238230
239- if ($ this ->isUnary ($ token )) {
240- $ operator = $ this ->unaryOperators [$ token ->getValue ()];
231+ if ($ token ->test (Token::OPERATOR_TYPE ) && $ operator = $ this ->operators ->getUnary ($ token ->getValue ())) {
241232 $ this ->parser ->getStream ()->next ();
242- $ expr = $ this ->parseExpression ($ operator[ ' precedence ' ] );
243- $ class = $ operator[ ' class ' ] ;
233+ $ expr = $ this ->parseExpression ($ operator-> getPrecedence () );
234+ $ class = $ operator-> getNodeClass () ;
244235
245236 $ expr = new $ class ($ expr , $ token ->getLine ());
246- $ expr ->setAttribute ('operator ' , ' unary_ ' .$ token ->getValue ());
237+ $ expr ->setAttribute ('operator ' , OperatorArity::Unary-> value . ' _ ' .$ token ->getValue ());
247238
248239 if ($ this ->deprecationCheck ) {
249240 $ this ->triggerPrecedenceDeprecations ($ expr );
@@ -284,16 +275,6 @@ private function parseConditionalExpression($expr): AbstractExpression
284275 return $ expr ;
285276 }
286277
287- private function isUnary (Token $ token ): bool
288- {
289- return $ token ->test (Token::OPERATOR_TYPE ) && isset ($ this ->unaryOperators [$ token ->getValue ()]);
290- }
291-
292- private function isBinary (Token $ token ): bool
293- {
294- return $ token ->test (Token::OPERATOR_TYPE ) && isset ($ this ->binaryOperators [$ token ->getValue ()]);
295- }
296-
297278 public function parsePrimaryExpression ()
298279 {
299280 $ token = $ this ->parser ->getCurrentToken ();
0 commit comments