Issues (67)

src/LL1Analyzer.php (1 issue)

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
Are you sure the assignment to $set is correct as $t->label() targeting Antlr\Antlr4\Runtime\Atn...ons\Transition::label() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

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.

Loading history...
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