1 | <?php |
||
2 | |||
3 | declare(strict_types=1); |
||
4 | |||
5 | namespace Antlr\Antlr4\Runtime; |
||
6 | |||
7 | use Antlr\Antlr4\Runtime\Atn\ATN; |
||
8 | use Antlr\Antlr4\Runtime\Atn\ATNConfig; |
||
9 | use Antlr\Antlr4\Runtime\Atn\States\ATNState; |
||
10 | use Antlr\Antlr4\Runtime\Atn\States\RuleStopState; |
||
11 | use Antlr\Antlr4\Runtime\Atn\Transitions\AbstractPredicateTransition; |
||
12 | use Antlr\Antlr4\Runtime\Atn\Transitions\NotSetTransition; |
||
13 | use Antlr\Antlr4\Runtime\Atn\Transitions\RuleTransition; |
||
14 | use Antlr\Antlr4\Runtime\Atn\Transitions\Transition; |
||
15 | use Antlr\Antlr4\Runtime\Atn\Transitions\WildcardTransition; |
||
16 | use Antlr\Antlr4\Runtime\PredictionContexts\PredictionContext; |
||
17 | use Antlr\Antlr4\Runtime\PredictionContexts\SingletonPredictionContext; |
||
18 | use Antlr\Antlr4\Runtime\Utils\BitSet; |
||
19 | use Antlr\Antlr4\Runtime\Utils\Set; |
||
20 | |||
21 | class LL1Analyzer |
||
22 | { |
||
23 | /** |
||
24 | * Special value added to the lookahead sets to indicate that we hit |
||
25 | * a predicate during analysis if `seeThruPreds === false`. |
||
26 | */ |
||
27 | public const HIT_PRED = Token::INVALID_TYPE; |
||
28 | |||
29 | /** @var ATN */ |
||
30 | public $atn; |
||
31 | |||
32 | 2 | public function __construct(ATN $atn) |
|
33 | { |
||
34 | 2 | $this->atn = $atn; |
|
35 | 2 | } |
|
36 | |||
37 | /** |
||
38 | * Calculates the SLL(1) expected lookahead set for each outgoing transition |
||
39 | * of an {@see ATNState}. The returned array has one element for each |
||
40 | * outgoing transition in `s`. If the closure from transition |
||
41 | * `i` leads to a semantic predicate before matching a symbol, the |
||
42 | * element at index `i` of the result will be `null`. |
||
43 | * |
||
44 | * @param ATNState|null $s The ATN state |
||
45 | * |
||
46 | * @return array<IntervalSet|null>|null The expected symbols for |
||
47 | * each outgoing transition of `s`. |
||
48 | */ |
||
49 | public function getDecisionLookahead(?ATNState $s) : ?array |
||
50 | { |
||
51 | if ($s === null) { |
||
52 | return null; |
||
53 | } |
||
54 | |||
55 | $look = []; |
||
56 | for ($alt = 0; $alt < $s->getNumberOfTransitions(); $alt++) { |
||
57 | $interval = new IntervalSet(); |
||
58 | $lookBusy = new Set(); |
||
59 | $seeThruPreds = false; // fail to get lookahead upon pred |
||
60 | |||
61 | $this->lookRecursively( |
||
62 | $s->getTransition($alt)->target, |
||
63 | null, |
||
64 | PredictionContext::empty(), |
||
65 | $interval, |
||
66 | $lookBusy, |
||
67 | new BitSet(), |
||
68 | $seeThruPreds, |
||
69 | false |
||
70 | ); |
||
71 | |||
72 | // Wipe out lookahead for this alternative if we found nothing |
||
73 | // or we had a predicate when we !seeThruPreds |
||
74 | if ($interval->length() === 0 || $interval->contains(self::HIT_PRED)) { |
||
75 | $look[$alt] = null; |
||
76 | } else { |
||
77 | $look[$alt] = $interval; |
||
78 | } |
||
79 | } |
||
80 | |||
81 | return $look; |
||
82 | } |
||
83 | |||
84 | /** |
||
85 | * Compute set of tokens that can follow `s` in the ATN in the |
||
86 | * specified `context`. |
||
87 | * |
||
88 | * If `context` is `null` and the end of the rule containing |
||
89 | * `s` is reached, {@see Token::EPSILON} is added to the result set. |
||
90 | * If `context` is not `null` and the end of the outermost rule is |
||
91 | * reached, {@see Token::EOF} is added to the result set. |
||
92 | * |
||
93 | * @param ATNState $s The ATN state. |
||
94 | * @param ATNState|null $stopState The ATN state to stop at. This can be |
||
95 | * a {@see BlockEndState} to detect |
||
96 | * epsilon paths through a closure. |
||
97 | * @param RuleContext|null $context The complete parser context, or `null` |
||
98 | * if the context should be ignored. |
||
99 | * |
||
100 | * @return IntervalSet The set of tokens that can follow `s` in the ATN |
||
101 | * in the specified `context`. |
||
102 | */ |
||
103 | 2 | public function look(ATNState $s, ?ATNState $stopState, ?RuleContext $context) : IntervalSet |
|
104 | { |
||
105 | 2 | $r = new IntervalSet(); |
|
106 | 2 | $seeThruPreds = true;// ignore preds; get all lookahead |
|
107 | |||
108 | 2 | $lookContext = $context !== null && $s->atn !== null ? |
|
109 | 1 | PredictionContext::fromRuleContext($s->atn, $context) : |
|
110 | 2 | null; |
|
111 | |||
112 | 2 | $this->lookRecursively( |
|
113 | 2 | $s, |
|
114 | $stopState, |
||
115 | $lookContext, |
||
116 | $r, |
||
117 | 2 | new Set(), |
|
118 | 2 | new BitSet(), |
|
119 | $seeThruPreds, |
||
120 | 2 | true |
|
121 | ); |
||
122 | |||
123 | 2 | return $r; |
|
124 | } |
||
125 | |||
126 | /** |
||
127 | * Compute set of tokens that can follow `s` in the ATN in the |
||
128 | * specified `context`. |
||
129 | * |
||
130 | * If `context` is `null` and `stopState` or the end of the |
||
131 | * rule containing `s` is reached, {@see Token::EPSILON} is added to |
||
132 | * the result set. If `context` is not `null` and `addEOF` is |
||
133 | * `true` and `stopState` or the end of the outermost rule is |
||
134 | * reached, {@see Token::EOF} is added to the result set. |
||
135 | * |
||
136 | * @param ATNState $s The ATN state. |
||
137 | * @param ATNState|null $stopState The ATN state to stop at. |
||
138 | * This can be a |
||
139 | * {@see BlockEndState} to |
||
140 | * detect epsilon paths |
||
141 | * through a closure. |
||
142 | * @param PredictionContext|null $context The outer context, or `null` |
||
143 | * if the outer context should |
||
144 | * not be used. |
||
145 | * @param IntervalSet $look The result lookahead set. |
||
146 | * @param Set $lookBusy A set used for preventing |
||
147 | * epsilon closures in the ATN |
||
148 | * from causing a stack overflow. |
||
149 | * Outside code should pass |
||
150 | * `new Set<ATNConfig>}` for |
||
151 | * this argument. |
||
152 | * @param BitSet $calledRuleStack A set used for preventing |
||
153 | * left recursion in the ATN |
||
154 | * from causing a stack overflow. |
||
155 | * Outside code should pass |
||
156 | * `new BitSet()` for |
||
157 | * this argument. |
||
158 | * @param bool $seeThruPreds `true` to true semantic |
||
159 | * predicates as implicitly |
||
160 | * `true` and "see through them", |
||
161 | * otherwise `false` to treat |
||
162 | * semantic predicates as |
||
163 | * opaque and add |
||
164 | * {@see self::HIT_PRED} to |
||
165 | * the result if one is |
||
166 | * encountered. |
||
167 | * @param bool $addEOF Add {@see Token::EOF} to |
||
168 | * the result if the end of |
||
169 | * the outermost context is |
||
170 | * reached. This parameter |
||
171 | * has no effect if `context` |
||
172 | * is `null`. |
||
173 | */ |
||
174 | 2 | protected function lookRecursively( |
|
175 | ATNState $s, |
||
176 | ?ATNState $stopState, |
||
177 | ?PredictionContext $context, |
||
178 | IntervalSet $look, |
||
179 | Set $lookBusy, |
||
180 | BitSet $calledRuleStack, |
||
181 | bool $seeThruPreds, |
||
182 | bool $addEOF |
||
183 | ) : void { |
||
184 | 2 | $c = new ATNConfig(null, $s, $context, null, 0); |
|
185 | |||
186 | 2 | if (!$lookBusy->add($c)) { |
|
187 | return; |
||
188 | } |
||
189 | |||
190 | 2 | if ($stopState !== null && $s->equals($stopState)) { |
|
191 | if ($context === null) { |
||
192 | $look->addOne(Token::EPSILON); |
||
193 | |||
194 | return; |
||
195 | } |
||
196 | |||
197 | if ($context->isEmpty() && $addEOF) { |
||
198 | $look->addOne(Token::EOF); |
||
199 | |||
200 | return; |
||
201 | } |
||
202 | } |
||
203 | |||
204 | 2 | if ($s instanceof RuleStopState) { |
|
205 | 2 | if ($context === null) { |
|
206 | 2 | $look->addOne(Token::EPSILON); |
|
207 | |||
208 | 2 | return; |
|
209 | } |
||
210 | |||
211 | if ($context->isEmpty() && $addEOF) { |
||
212 | $look->addOne(Token::EOF); |
||
213 | |||
214 | return; |
||
215 | } |
||
216 | |||
217 | if ($context !== PredictionContext::empty()) { |
||
218 | // run thru all possible stack tops in ctx |
||
219 | $removed = $calledRuleStack->contains($s->ruleIndex); |
||
220 | |||
221 | try { |
||
222 | $calledRuleStack->remove($s->ruleIndex); |
||
223 | for ($i = 0; $i < $context->getLength(); $i++) { |
||
224 | $returnState = $this->atn->states[$context->getReturnState($i)]; |
||
225 | $this->lookRecursively( |
||
226 | $returnState, |
||
227 | $stopState, |
||
228 | $context->getParent($i), |
||
229 | $look, |
||
230 | $lookBusy, |
||
231 | $calledRuleStack, |
||
232 | $seeThruPreds, |
||
233 | $addEOF |
||
234 | ); |
||
235 | } |
||
236 | } finally { |
||
237 | if ($removed) { |
||
238 | $calledRuleStack->add($s->ruleIndex); |
||
239 | } |
||
240 | } |
||
241 | |||
242 | return; |
||
243 | } |
||
244 | } |
||
245 | |||
246 | /** @var Transition $t */ |
||
247 | 2 | foreach ($s->getTransitions() as $t) { |
|
248 | 2 | if ($t instanceof RuleTransition) { |
|
249 | 2 | if ($calledRuleStack->contains($t->target->ruleIndex)) { |
|
250 | continue; |
||
251 | } |
||
252 | |||
253 | 2 | $newContext = SingletonPredictionContext::create($context, $t->followState->stateNumber); |
|
254 | |||
255 | try { |
||
256 | 2 | $calledRuleStack->add($t->target->ruleIndex); |
|
257 | 2 | $this->lookRecursively( |
|
258 | 2 | $t->target, |
|
259 | $stopState, |
||
260 | $newContext, |
||
261 | $look, |
||
262 | $lookBusy, |
||
263 | $calledRuleStack, |
||
264 | $seeThruPreds, |
||
265 | $addEOF |
||
266 | ); |
||
267 | 2 | } finally { |
|
268 | 2 | $calledRuleStack->remove($t->target->ruleIndex); |
|
269 | } |
||
270 | 2 | } elseif ($t instanceof AbstractPredicateTransition) { |
|
271 | 2 | if ($seeThruPreds) { |
|
272 | 2 | $this->lookRecursively( |
|
273 | 2 | $t->target, |
|
274 | $stopState, |
||
275 | $context, |
||
276 | $look, |
||
277 | $lookBusy, |
||
278 | $calledRuleStack, |
||
279 | $seeThruPreds, |
||
280 | $addEOF |
||
281 | ); |
||
282 | } else { |
||
283 | 2 | $look->addOne(self::HIT_PRED); |
|
284 | } |
||
285 | 2 | } elseif ($t->isEpsilon()) { |
|
286 | 2 | $this->lookRecursively( |
|
287 | 2 | $t->target, |
|
288 | $stopState, |
||
289 | $context, |
||
290 | $look, |
||
291 | $lookBusy, |
||
292 | $calledRuleStack, |
||
293 | $seeThruPreds, |
||
294 | $addEOF |
||
295 | ); |
||
296 | 2 | } elseif ($t instanceof WildcardTransition) { |
|
297 | $look->addRange(Token::MIN_USER_TOKEN_TYPE, $this->atn->maxTokenType); |
||
298 | } else { |
||
299 | 2 | $set = $t->label(); |
|
0 ignored issues
–
show
|
|||
300 | |||
301 | 2 | if ($set !== null) { |
|
302 | 2 | if ($t instanceof NotSetTransition) { |
|
303 | $set = $set->complement(IntervalSet::fromRange( |
||
304 | Token::MIN_USER_TOKEN_TYPE, |
||
305 | $this->atn->maxTokenType |
||
306 | )); |
||
307 | } |
||
308 | |||
309 | 2 | if ($set !== null) { |
|
310 | 2 | $look->addSet($set); |
|
311 | } |
||
312 | } |
||
313 | } |
||
314 | } |
||
315 | 2 | } |
|
316 | } |
||
317 |
This check looks for function or method calls that always return null and whose return value is assigned to a variable.
The method
getObject()
can return nothing but null, so it makes no sense to assign that value to a variable.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.