@@ -16,24 +16,53 @@ class ConstExprParser
1616 /** @var bool */
1717 private $ quoteAwareConstExprString ;
1818
19- public function __construct (bool $ unescapeStrings = false , bool $ quoteAwareConstExprString = false )
19+ /** @var bool */
20+ private $ useLinesAttributes ;
21+
22+ /** @var bool */
23+ private $ useIndexAttributes ;
24+
25+ /**
26+ * @param array{lines?: bool, indexes?: bool} $usedAttributes
27+ */
28+ public function __construct (
29+ bool $ unescapeStrings = false ,
30+ bool $ quoteAwareConstExprString = false ,
31+ array $ usedAttributes = []
32+ )
2033 {
2134 $ this ->unescapeStrings = $ unescapeStrings ;
2235 $ this ->quoteAwareConstExprString = $ quoteAwareConstExprString ;
36+ $ this ->useLinesAttributes = $ usedAttributes ['lines ' ] ?? false ;
37+ $ this ->useIndexAttributes = $ usedAttributes ['indexes ' ] ?? false ;
2338 }
2439
2540 public function parse (TokenIterator $ tokens , bool $ trimStrings = false ): Ast \ConstExpr \ConstExprNode
2641 {
42+ $ startLine = $ tokens ->currentTokenLine ();
43+ $ startIndex = $ tokens ->currentTokenIndex ();
2744 if ($ tokens ->isCurrentTokenType (Lexer::TOKEN_FLOAT )) {
2845 $ value = $ tokens ->currentTokenValue ();
2946 $ tokens ->next ();
30- return new Ast \ConstExpr \ConstExprFloatNode ($ value );
47+
48+ return $ this ->enrichWithAttributes (
49+ $ tokens ,
50+ new Ast \ConstExpr \ConstExprFloatNode ($ value ),
51+ $ startLine ,
52+ $ startIndex
53+ );
3154 }
3255
3356 if ($ tokens ->isCurrentTokenType (Lexer::TOKEN_INTEGER )) {
3457 $ value = $ tokens ->currentTokenValue ();
3558 $ tokens ->next ();
36- return new Ast \ConstExpr \ConstExprIntegerNode ($ value );
59+
60+ return $ this ->enrichWithAttributes (
61+ $ tokens ,
62+ new Ast \ConstExpr \ConstExprIntegerNode ($ value ),
63+ $ startLine ,
64+ $ startIndex
65+ );
3766 }
3867
3968 if ($ tokens ->isCurrentTokenType (Lexer::TOKEN_SINGLE_QUOTED_STRING , Lexer::TOKEN_DOUBLE_QUOTED_STRING )) {
@@ -49,27 +78,52 @@ public function parse(TokenIterator $tokens, bool $trimStrings = false): Ast\Con
4978 $ tokens ->next ();
5079
5180 if ($ this ->quoteAwareConstExprString ) {
52- return new Ast \ConstExpr \QuoteAwareConstExprStringNode (
53- $ value ,
54- $ type === Lexer::TOKEN_SINGLE_QUOTED_STRING
55- ? Ast \ConstExpr \QuoteAwareConstExprStringNode::SINGLE_QUOTED
56- : Ast \ConstExpr \QuoteAwareConstExprStringNode::DOUBLE_QUOTED
81+ return $ this ->enrichWithAttributes (
82+ $ tokens ,
83+ new Ast \ConstExpr \QuoteAwareConstExprStringNode (
84+ $ value ,
85+ $ type === Lexer::TOKEN_SINGLE_QUOTED_STRING
86+ ? Ast \ConstExpr \QuoteAwareConstExprStringNode::SINGLE_QUOTED
87+ : Ast \ConstExpr \QuoteAwareConstExprStringNode::DOUBLE_QUOTED
88+ ),
89+ $ startLine ,
90+ $ startIndex
5791 );
5892 }
5993
60- return new Ast \ConstExpr \ConstExprStringNode ($ value );
94+ return $ this ->enrichWithAttributes (
95+ $ tokens ,
96+ new Ast \ConstExpr \ConstExprStringNode ($ value ),
97+ $ startLine ,
98+ $ startIndex
99+ );
61100
62101 } elseif ($ tokens ->isCurrentTokenType (Lexer::TOKEN_IDENTIFIER )) {
63102 $ identifier = $ tokens ->currentTokenValue ();
64103 $ tokens ->next ();
65104
66105 switch (strtolower ($ identifier )) {
67106 case 'true ' :
68- return new Ast \ConstExpr \ConstExprTrueNode ();
107+ return $ this ->enrichWithAttributes (
108+ $ tokens ,
109+ new Ast \ConstExpr \ConstExprTrueNode (),
110+ $ startLine ,
111+ $ startIndex
112+ );
69113 case 'false ' :
70- return new Ast \ConstExpr \ConstExprFalseNode ();
114+ return $ this ->enrichWithAttributes (
115+ $ tokens ,
116+ new Ast \ConstExpr \ConstExprFalseNode (),
117+ $ startLine ,
118+ $ startIndex
119+ );
71120 case 'null ' :
72- return new Ast \ConstExpr \ConstExprNullNode ();
121+ return $ this ->enrichWithAttributes (
122+ $ tokens ,
123+ new Ast \ConstExpr \ConstExprNullNode (),
124+ $ startLine ,
125+ $ startIndex
126+ );
73127 case 'array ' :
74128 $ tokens ->consumeTokenType (Lexer::TOKEN_OPEN_PARENTHESES );
75129 return $ this ->parseArray ($ tokens , Lexer::TOKEN_CLOSE_PARENTHESES );
@@ -106,11 +160,21 @@ public function parse(TokenIterator $tokens, bool $trimStrings = false): Ast\Con
106160 break ;
107161 }
108162
109- return new Ast \ConstExpr \ConstFetchNode ($ identifier , $ classConstantName );
163+ return $ this ->enrichWithAttributes (
164+ $ tokens ,
165+ new Ast \ConstExpr \ConstFetchNode ($ identifier , $ classConstantName ),
166+ $ startLine ,
167+ $ startIndex
168+ );
110169
111170 }
112171
113- return new Ast \ConstExpr \ConstFetchNode ('' , $ identifier );
172+ return $ this ->enrichWithAttributes (
173+ $ tokens ,
174+ new Ast \ConstExpr \ConstFetchNode ('' , $ identifier ),
175+ $ startLine ,
176+ $ startIndex
177+ );
114178
115179 } elseif ($ tokens ->tryConsumeTokenType (Lexer::TOKEN_OPEN_SQUARE_BRACKET )) {
116180 return $ this ->parseArray ($ tokens , Lexer::TOKEN_CLOSE_SQUARE_BRACKET );
@@ -131,19 +195,30 @@ private function parseArray(TokenIterator $tokens, int $endToken): Ast\ConstExpr
131195 {
132196 $ items = [];
133197
198+ $ startLine = $ tokens ->currentTokenLine ();
199+ $ startIndex = $ tokens ->currentTokenIndex ();
200+
134201 if (!$ tokens ->tryConsumeTokenType ($ endToken )) {
135202 do {
136203 $ items [] = $ this ->parseArrayItem ($ tokens );
137204 } while ($ tokens ->tryConsumeTokenType (Lexer::TOKEN_COMMA ) && !$ tokens ->isCurrentTokenType ($ endToken ));
138205 $ tokens ->consumeTokenType ($ endToken );
139206 }
140207
141- return new Ast \ConstExpr \ConstExprArrayNode ($ items );
208+ return $ this ->enrichWithAttributes (
209+ $ tokens ,
210+ new Ast \ConstExpr \ConstExprArrayNode ($ items ),
211+ $ startLine ,
212+ $ startIndex
213+ );
142214 }
143215
144216
145217 private function parseArrayItem (TokenIterator $ tokens ): Ast \ConstExpr \ConstExprArrayItemNode
146218 {
219+ $ startLine = $ tokens ->currentTokenLine ();
220+ $ startIndex = $ tokens ->currentTokenIndex ();
221+
147222 $ expr = $ this ->parse ($ tokens );
148223
149224 if ($ tokens ->tryConsumeTokenType (Lexer::TOKEN_DOUBLE_ARROW )) {
@@ -155,7 +230,40 @@ private function parseArrayItem(TokenIterator $tokens): Ast\ConstExpr\ConstExprA
155230 $ value = $ expr ;
156231 }
157232
158- return new Ast \ConstExpr \ConstExprArrayItemNode ($ key , $ value );
233+ return $ this ->enrichWithAttributes (
234+ $ tokens ,
235+ new Ast \ConstExpr \ConstExprArrayItemNode ($ key , $ value ),
236+ $ startLine ,
237+ $ startIndex
238+ );
239+ }
240+
241+ /**
242+ * @template T of Ast\ConstExpr\ConstExprNode
243+ * @param T $node
244+ * @return T
245+ */
246+ private function enrichWithAttributes (TokenIterator $ tokens , Ast \ConstExpr \ConstExprNode $ node , int $ startLine , int $ startIndex ): Ast \ConstExpr \ConstExprNode
247+ {
248+ $ endLine = $ tokens ->currentTokenLine ();
249+ $ endIndex = $ tokens ->currentTokenIndex ();
250+ if ($ this ->useLinesAttributes ) {
251+ $ node ->setAttribute (Ast \Attribute::START_LINE , $ startLine );
252+ $ node ->setAttribute (Ast \Attribute::END_LINE , $ endLine );
253+ }
254+
255+ if ($ this ->useIndexAttributes ) {
256+ $ tokensArray = $ tokens ->getTokens ();
257+ $ endIndex --;
258+ if ($ tokensArray [$ endIndex ][Lexer::TYPE_OFFSET ] === Lexer::TOKEN_HORIZONTAL_WS ) {
259+ $ endIndex --;
260+ }
261+
262+ $ node ->setAttribute (Ast \Attribute::START_INDEX , $ startIndex );
263+ $ node ->setAttribute (Ast \Attribute::END_INDEX , $ endIndex );
264+ }
265+
266+ return $ node ;
159267 }
160268
161269}
0 commit comments