LL1Analyzer::lookRecursively()   D
last analyzed

Complexity

Conditions 24
Paths 83

Size

Total Lines 137
Code Lines 88

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 31
CRAP Score 82.1007

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 24
eloc 88
c 1
b 0
f 0
nc 83
nop 8
dl 0
loc 137
ccs 31
cts 58
cp 0.5345
crap 82.1007
rs 4.1666

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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()) {
0 ignored issues
show
introduced by
The condition $context !== Antlr\Antlr...dictionContext::empty() is always true.
Loading history...
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
Bug introduced by
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