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 ;
2524use Twig \Node \Expression \MacroReferenceExpression ;
2625use Twig \Node \Expression \NameExpression ;
2726use Twig \Node \Expression \Ternary \ConditionalTernary ;
2827use Twig \Node \Expression \TestExpression ;
29- use Twig \Node \Expression \Unary \AbstractUnary ;
3028use Twig \Node \Expression \Unary \NegUnary ;
3129use Twig \Node \Expression \Unary \NotUnary ;
3230use Twig \Node \Expression \Unary \PosUnary ;
3735use Twig \Node \Expression \Variable \TemplateVariable ;
3836use Twig \Node \Node ;
3937use Twig \Node \Nodes ;
38+ use Twig \Operator \OperatorArity ;
39+ use Twig \Operator \OperatorAssociativity ;
40+ use Twig \Operator \Operators ;
4041
4142/**
4243 * Parses expressions.
5051 */
5152class ExpressionParser
5253{
54+ // FIXME: deprecated, use OperatorAssociativity instead
5355 public const OPERATOR_LEFT = 1 ;
5456 public const OPERATOR_RIGHT = 2 ;
5557
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 ;
58+ private Operators $ operators ;
6059 private $ readyNodes = [];
6160 private array $ precedenceChanges = [];
6261 private bool $ deprecationCheck = true ;
@@ -65,26 +64,16 @@ public function __construct(
6564 private Parser $ parser ,
6665 private Environment $ env ,
6766 ) {
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 ' ])) {
67+ $ this ->operators = $ env ->getOperators ();
68+ foreach ($ this ->operators as $ name => $ op ) {
69+ if (!$ op ->getPrecedenceChange ()) {
8070 continue ;
8171 }
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 ;
72+ $ min = min ($ op ->getPrecedenceChange ()->getNewPrecedence (), $ op ->getPrecedence ());
73+ $ max = max ($ op ->getPrecedenceChange ()->getNewPrecedence (), $ op ->getPrecedence ());
74+ foreach ($ this ->operators as $ n => $ o ) {
75+ if ($ o ->getPrecedence () > $ min && $ o ->getPrecedence () < $ max ) {
76+ $ this ->precedenceChanges [$ n ][] = $ name ;
8877 }
8978 }
9079 }
@@ -102,28 +91,27 @@ public function parseExpression($precedence = 0)
10291
10392 $ expr = $ this ->getPrimary ();
10493 $ token = $ this ->parser ->getCurrentToken ();
105- while ($ this ->isBinary ($ token ) && $ this ->binaryOperators [$ token ->getValue ()]['precedence ' ] >= $ precedence ) {
106- $ op = $ this ->binaryOperators [$ token ->getValue ()];
94+ while ($ token ->test (Token::OPERATOR_TYPE ) && ($ op = $ this ->operators ->getBinary ($ token ->getValue ())) && $ op ->getPrecedence () >= $ precedence ) {
10795 $ this ->parser ->getStream ()->next ();
10896
10997 if ('is not ' === $ token ->getValue ()) {
11098 $ expr = $ this ->parseNotTestExpression ($ expr );
11199 } elseif ('is ' === $ token ->getValue ()) {
112100 $ expr = $ this ->parseTestExpression ($ expr );
113- } elseif (isset ( $ op[ ' callable ' ] )) {
114- $ expr = $ op[ ' callable ' ] ($ this ->parser , $ expr );
101+ } elseif (null !== $ op-> getCallable ( )) {
102+ $ expr = $ op-> getCallable () ($ this ->parser , $ expr );
115103 } else {
116104 $ previous = $ this ->setDeprecationCheck (true );
117105 try {
118- $ expr1 = $ this ->parseExpression (self :: OPERATOR_LEFT === $ op[ ' associativity ' ] ? $ op[ ' precedence ' ] + 1 : $ op[ ' precedence ' ] );
106+ $ expr1 = $ this ->parseExpression (OperatorAssociativity::Left === $ op-> getAssociativity () ? $ op-> getPrecedence () + 1 : $ op-> getPrecedence () );
119107 } finally {
120108 $ this ->setDeprecationCheck ($ previous );
121109 }
122- $ class = $ op[ ' class ' ] ;
110+ $ class = $ op-> getNodeClass () ;
123111 $ expr = new $ class ($ expr , $ expr1 , $ token ->getLine ());
124112 }
125113
126- $ expr ->setAttribute ('operator ' , ' binary_ ' .$ token ->getValue ());
114+ $ expr ->setAttribute ('operator ' , OperatorArity::Binary-> value . ' _ ' .$ token ->getValue ());
127115
128116 $ this ->triggerPrecedenceDeprecations ($ expr );
129117
@@ -156,7 +144,7 @@ private function triggerPrecedenceDeprecations(AbstractExpression $expr): void
156144 continue ;
157145 }
158146 if ($ node ->hasAttribute ('operator ' ) && $ operatorName === $ node ->getAttribute ('operator ' )) {
159- $ change = $ this ->unaryOperators [ $ target][ ' precedence_change ' ] ;
147+ $ change = $ this ->operators -> getUnary ( $ target)-> getPrecedenceChange () ;
160148 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 ()));
161149 }
162150 }
@@ -166,7 +154,7 @@ private function triggerPrecedenceDeprecations(AbstractExpression $expr): void
166154 /** @var AbstractExpression $node */
167155 if ($ node ->hasAttribute ('operator ' ) && $ operatorName === $ node ->getAttribute ('operator ' ) && !$ node ->hasExplicitParentheses ()) {
168156 $ op = explode ('_ ' , $ operatorName )[1 ];
169- $ change = $ this ->binaryOperators [ $ op][ ' precedence_change ' ] ;
157+ $ change = $ this ->operators -> getBinary ( $ op)-> getPrecedenceChange () ;
170158 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 ()));
171159 }
172160 }
@@ -236,14 +224,13 @@ private function getPrimary(): AbstractExpression
236224 {
237225 $ token = $ this ->parser ->getCurrentToken ();
238226
239- if ($ this ->isUnary ($ token )) {
240- $ operator = $ this ->unaryOperators [$ token ->getValue ()];
227+ if ($ token ->test (Token::OPERATOR_TYPE ) && $ operator = $ this ->operators ->getUnary ($ token ->getValue ())) {
241228 $ this ->parser ->getStream ()->next ();
242- $ expr = $ this ->parseExpression ($ operator[ ' precedence ' ] );
243- $ class = $ operator[ ' class ' ] ;
229+ $ expr = $ this ->parseExpression ($ operator-> getPrecedence () );
230+ $ class = $ operator-> getNodeClass () ;
244231
245232 $ expr = new $ class ($ expr , $ token ->getLine ());
246- $ expr ->setAttribute ('operator ' , ' unary_ ' .$ token ->getValue ());
233+ $ expr ->setAttribute ('operator ' , OperatorArity::Unary-> value . ' _ ' .$ token ->getValue ());
247234
248235 if ($ this ->deprecationCheck ) {
249236 $ this ->triggerPrecedenceDeprecations ($ expr );
@@ -284,16 +271,6 @@ private function parseConditionalExpression($expr): AbstractExpression
284271 return $ expr ;
285272 }
286273
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-
297274 public function parsePrimaryExpression ()
298275 {
299276 $ token = $ this ->parser ->getCurrentToken ();
0 commit comments