ATNDeserializer::lexerActionFactory()   B
last analyzed

Complexity

Conditions 9
Paths 9

Size

Total Lines 31
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 22.4762

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 21
c 1
b 0
f 0
nc 9
nop 3
dl 0
loc 31
ccs 9
cts 20
cp 0.45
crap 22.4762
rs 8.0555
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Antlr\Antlr4\Runtime\Atn;
6
7
use Antlr\Antlr4\Runtime\Atn\Actions\LexerAction;
8
use Antlr\Antlr4\Runtime\Atn\Actions\LexerActionType;
9
use Antlr\Antlr4\Runtime\Atn\Actions\LexerChannelAction;
10
use Antlr\Antlr4\Runtime\Atn\Actions\LexerCustomAction;
11
use Antlr\Antlr4\Runtime\Atn\Actions\LexerModeAction;
12
use Antlr\Antlr4\Runtime\Atn\Actions\LexerMoreAction;
13
use Antlr\Antlr4\Runtime\Atn\Actions\LexerPopModeAction;
14
use Antlr\Antlr4\Runtime\Atn\Actions\LexerPushModeAction;
15
use Antlr\Antlr4\Runtime\Atn\Actions\LexerSkipAction;
16
use Antlr\Antlr4\Runtime\Atn\Actions\LexerTypeAction;
17
use Antlr\Antlr4\Runtime\Atn\States\ATNState;
18
use Antlr\Antlr4\Runtime\Atn\States\BasicBlockStartState;
19
use Antlr\Antlr4\Runtime\Atn\States\BasicState;
20
use Antlr\Antlr4\Runtime\Atn\States\BlockEndState;
21
use Antlr\Antlr4\Runtime\Atn\States\BlockStartState;
22
use Antlr\Antlr4\Runtime\Atn\States\DecisionState;
23
use Antlr\Antlr4\Runtime\Atn\States\LoopEndState;
24
use Antlr\Antlr4\Runtime\Atn\States\PlusBlockStartState;
25
use Antlr\Antlr4\Runtime\Atn\States\PlusLoopbackState;
26
use Antlr\Antlr4\Runtime\Atn\States\RuleStartState;
27
use Antlr\Antlr4\Runtime\Atn\States\RuleStopState;
28
use Antlr\Antlr4\Runtime\Atn\States\StarBlockStartState;
29
use Antlr\Antlr4\Runtime\Atn\States\StarLoopbackState;
30
use Antlr\Antlr4\Runtime\Atn\States\StarLoopEntryState;
31
use Antlr\Antlr4\Runtime\Atn\States\TokensStartState;
32
use Antlr\Antlr4\Runtime\Atn\Transitions\ActionTransition;
33
use Antlr\Antlr4\Runtime\Atn\Transitions\AtomTransition;
34
use Antlr\Antlr4\Runtime\Atn\Transitions\EpsilonTransition;
35
use Antlr\Antlr4\Runtime\Atn\Transitions\NotSetTransition;
36
use Antlr\Antlr4\Runtime\Atn\Transitions\PrecedencePredicateTransition;
37
use Antlr\Antlr4\Runtime\Atn\Transitions\PredicateTransition;
38
use Antlr\Antlr4\Runtime\Atn\Transitions\RangeTransition;
39
use Antlr\Antlr4\Runtime\Atn\Transitions\RuleTransition;
40
use Antlr\Antlr4\Runtime\Atn\Transitions\SetTransition;
41
use Antlr\Antlr4\Runtime\Atn\Transitions\Transition;
42
use Antlr\Antlr4\Runtime\Atn\Transitions\WildcardTransition;
43
use Antlr\Antlr4\Runtime\IntervalSet;
44
use Antlr\Antlr4\Runtime\Token;
45
use Antlr\Antlr4\Runtime\Utils\StringUtils;
46
47
final class ATNDeserializer
48
{
49
    /**
50
     * This value should never change. Updates following this version are
51
     * reflected as change in the unique ID SERIALIZED_UUID.
52
     */
53
    public const SERIALIZED_VERSION = 3;
54
55
    /**
56
     * This is the earliest supported serialized UUID.
57
     * Stick to serialized version for now, we don't need a UUID instance.
58
     */
59
    private const BASE_SERIALIZED_UUID = 'AADB8D7E-AEEF-4415-AD2B-8204D6CF042E';
60
61
    /**
62
     * This UUID indicates the serialized ATN contains two sets of IntervalSets,
63
     * where the second set's values are encoded as 32-bit integers to support
64
     * the full Unicode SMP range up to U+10FFFF.
65
     */
66
    private const ADDED_UNICODE_SMP = '59627784-3BE5-417A-B9EB-8131A7286089';
67
68
    /**
69
     * This list contains all of the currently supported UUIDs, ordered by when
70
     * the feature first appeared in this branch.
71
     */
72
    private const SUPPORTED_UUIDS = [
73
        self::BASE_SERIALIZED_UUID,
74
        self::ADDED_UNICODE_SMP,
75
    ];
76
77
    /**
78
     * This is the current serialized UUID.
79
     */
80
    private const SERIALIZED_UUID = self::ADDED_UNICODE_SMP;
81
82
    /** @var ATNDeserializationOptions */
83
    private $deserializationOptions;
84
85
    /** @var array<int> */
86
    private $data = [];
87
88
    /** @var int */
89
    private $pos = 0;
90
91
    /** @var string */
92
    private $uuid = '';
93
94
    /** @var array<int, callable|null>|null */
95
    private $stateFactories;
0 ignored issues
show
introduced by
The private property $stateFactories is not used, and could be removed.
Loading history...
96
97
    /** @var array<int, callable|null>|null */
98
    private $actionFactories;
0 ignored issues
show
introduced by
The private property $actionFactories is not used, and could be removed.
Loading history...
99
100 1
    public function __construct(?ATNDeserializationOptions $options = null)
101
    {
102 1
        $this->deserializationOptions = $options ?? ATNDeserializationOptions::defaultOptions();
103 1
    }
104
105
    /**
106
     * Determines if a particular serialized representation of an ATN supports
107
     * a particular feature, identified by the {@see UUID} used for serializing
108
     * the ATN at the time the feature was first introduced.
109
     *
110
     * @param string $feature    The {@see UUID} marking the first time the
111
     *                           feature was supported in the serialized ATN.
112
     * @param string $actualUuid The {@see UUID} of the actual serialized ATN
113
     *                           which is currently being deserialized.
114
     *
115
     * @return bool `true` if the `actualUuid` value represents a serialized
116
     *              ATN at or after the feature identified by `feature` was
117
     *              introduced; otherwise, `false`.
118
     */
119 1
    protected function isFeatureSupported(string $feature, string $actualUuid) : bool
120
    {
121 1
        $featureIndex = \array_search($feature, self::SUPPORTED_UUIDS, true);
122
123 1
        if ($featureIndex === false) {
124
            return false;
125
        }
126
127 1
        $actualUuidIndex = \array_search($actualUuid, self::SUPPORTED_UUIDS, true);
128
129 1
        return $actualUuidIndex >= $featureIndex;
130
    }
131
132 1
    public function deserialize(string $data) : ATN
133
    {
134 1
        $this->reset($data);
135 1
        $this->checkVersion();
136 1
        $this->checkUUID();
137 1
        $atn = $this->readATN();
138 1
        $this->readStates($atn);
139 1
        $this->readRules($atn);
140 1
        $this->readModes($atn);
141 1
        $sets = [];
142
143
        // First, deserialize sets with 16-bit arguments <= U+FFFF.
144 1
        $this->readSets($sets, function () {
145 1
            return $this->readInt();
146 1
        });
147
148
        // Next, if the ATN was serialized with the Unicode SMP feature,
149
        // deserialize sets with 32-bit arguments <= U+10FFFF.
150
151 1
        if ($this->isFeatureSupported(self::ADDED_UNICODE_SMP, $this->uuid)) {
152 1
            $this->readSets($sets, function () {
153
                return $this->readInt32();
154 1
            });
155
        }
156
157 1
        $this->readEdges($atn, $sets);
158 1
        $this->readDecisions($atn);
159 1
        $this->readLexerActions($atn);
160 1
        $this->markPrecedenceDecisions($atn);
161 1
        $this->verifyATN($atn);
162
163 1
        if ($atn->grammarType === ATN::ATN_TYPE_PARSER
164 1
            && $this->deserializationOptions->isGenerateRuleBypassTransitions()) {
165
            $this->generateRuleBypassTransitions($atn);
166
            // re-verify after modification
167
            $this->verifyATN($atn);
168
        }
169
170 1
        return $atn;
171
    }
172
173 1
    private function reset(string $data) : void
174
    {
175 1
        $characters = \preg_split('//u', $data, -1, \PREG_SPLIT_NO_EMPTY);
176
177 1
        if ($characters === false) {
178
            return;
179
        }
180
181 1
        $this->data = [StringUtils::codePoint($characters[0])];
182 1
        for ($i = 1, $length = \count($characters); $i < $length; $i++) {
183 1
            $code = StringUtils::codePoint($characters[$i]);
184 1
            $this->data[] = $code > 1  ? $code - 2 : $code + 65533;
185
        }
186
187 1
        $this->pos = 0;
188 1
    }
189
190
191 1
    private function checkVersion() : void
192
    {
193 1
        $version = $this->readInt();
194
195 1
        if ($version !== self::SERIALIZED_VERSION) {
196
            throw new \InvalidArgumentException(\sprintf(
197
                'Could not deserialize ATN with version %d (expected %d).',
198
                $version,
199
                self::SERIALIZED_VERSION
200
            ));
201
        }
202 1
    }
203
204 1
    private function checkUUID() : void
205
    {
206 1
        $uuid = $this->readUUID();
207
208 1
        if (!\in_array($uuid, self::SUPPORTED_UUIDS, true)) {
209
            throw new \InvalidArgumentException(\sprintf(
210
                'Could not deserialize ATN with UUID: %s (expected %s or a legacy UUID).',
211
                $uuid,
212
                self::SERIALIZED_UUID
213
            ));
214
        }
215
216 1
        $this->uuid = $uuid;
217 1
    }
218
219 1
    private function readATN() : ATN
220
    {
221 1
        $grammarType = $this->readInt();
222 1
        $maxTokenType = $this->readInt();
223
224 1
        return new ATN($grammarType, $maxTokenType);
225
    }
226
227 1
    private function readStates(ATN $atn) : void
228
    {
229 1
        $loopBackStateNumbers = [];
230 1
        $endStateNumbers = [];
231 1
        $nstates = $this->readInt();
232
233 1
        for ($i=0; $i < $nstates; $i++) {
234 1
            $stype = $this->readInt();
235
236
            // ignore bad type of states
237 1
            if ($stype === ATNState::INVALID_TYPE) {
238
                $atn->addState(null);
239
240
                continue;
241
            }
242
243 1
            $ruleIndex = $this->readInt();
244
245 1
            if ($ruleIndex === 0xFFFF) {
246
                $ruleIndex = -1;
247
            }
248
249 1
            $s = $this->stateFactory($stype, $ruleIndex);
250
251 1
            if ($stype === ATNState::LOOP_END) {
252
                // special case
253 1
                $loopBackStateNumber = $this->readInt();
254
255 1
                if (!$s instanceof LoopEndState) {
256
                    throw new \RuntimeException('Unexpected ATN State');
257
                }
258
259 1
                $loopBackStateNumbers[] = [$s, $loopBackStateNumber];
260 1
            } elseif ($s instanceof BlockStartState) {
261 1
                $endStateNumber = $this->readInt();
262
263 1
                $endStateNumbers[] = [$s, $endStateNumber];
264
            }
265
266 1
            $atn->addState($s);
267
        }
268
269
        // delay the assignment of loop back and end states until we know all the
270
        // state instances have been initialized
271 1
        foreach ($loopBackStateNumbers as $pair) {
272 1
            $pair[0]->loopBackState = $atn->states[$pair[1]];
273
        }
274
275 1
        foreach ($endStateNumbers as $pair) {
276 1
            $endState = $atn->states[$pair[1]];
277
278 1
            if (!$endState instanceof BlockEndState) {
279
                throw new \RuntimeException('Unexpected ATN State');
280
            }
281
282 1
            $pair[0]->endState = $endState;
283
        }
284
285 1
        $numNonGreedyStates = $this->readInt();
286
287 1
        for ($j=0; $j < $numNonGreedyStates; $j++) {
288
            $decisionState = $atn->states[$this->readInt()];
289
290
            if (!$decisionState instanceof DecisionState) {
291
                throw new \RuntimeException('Unexpected ATN State');
292
            }
293
294
            $decisionState->nonGreedy = true;
295
        }
296
297 1
        $numPrecedenceStates = $this->readInt();
298
299 1
        for ($j=0; $j < $numPrecedenceStates; $j++) {
300 1
            $ruleStartState = $atn->states[$this->readInt()];
301
302 1
            if (!$ruleStartState instanceof RuleStartState) {
303
                throw new \RuntimeException('Unexpected ATN State');
304
            }
305
306 1
            $ruleStartState->isLeftRecursiveRule = true;
307
        }
308 1
    }
309
310 1
    private function readRules(ATN $atn) : void
311
    {
312 1
        $nRules = $this->readInt();
313
314 1
        $atn->ruleToTokenType = [];
315 1
        $atn->ruleToStartState = [];
316 1
        for ($i=0; $i < $nRules; $i++) {
317 1
            $s = $this->readInt();
318 1
            $startState = $atn->states[$s];
319
320 1
            if (!$startState instanceof RuleStartState) {
321
                throw new \RuntimeException('Unexpected ATN State');
322
            }
323
324 1
            $atn->ruleToStartState[$i] = $startState;
325
326 1
            if ($atn->grammarType === ATN::ATN_TYPE_LEXER) {
327 1
                $tokenType = $this->readInt();
328
329 1
                if ($tokenType === 0xFFFF) {
330
                    $tokenType = Token::EOF;
331
                }
332
333 1
                $atn->ruleToTokenType[$i] = $tokenType;
334
            }
335
        }
336
337 1
        $atn->ruleToStopState = [];
338 1
        foreach ($atn->states as $state) {
339 1
            if (!$state instanceof RuleStopState) {
340 1
                continue;
341
            }
342
343 1
            $atn->ruleToStopState[$state->ruleIndex] = $state;
344 1
            $atn->ruleToStartState[$state->ruleIndex]->stopState = $state;
345
        }
346 1
    }
347
348 1
    private function readModes(ATN $atn) : void
349
    {
350 1
        $nmodes = $this->readInt();
351
352 1
        for ($i=0; $i < $nmodes; $i++) {
353 1
            $tokensStartState = $atn->states[$this->readInt()];
354
355 1
            if (!$tokensStartState instanceof TokensStartState) {
356
                throw new \RuntimeException('Unexpected ATN State');
357
            }
358
359 1
            $atn->modeToStartState[] = $tokensStartState;
360
        }
361 1
    }
362
363
    /**
364
     * @param array<IntervalSet> $sets
365
     */
366 1
    private function readSets(array &$sets, callable $readUnicode) : void
367
    {
368 1
        $m = $this->readInt();
369
370 1
        for ($i=0; $i < $m; $i++) {
371 1
            $iset = new IntervalSet();
372
373 1
            $sets[] = $iset;
374 1
            $n = $this->readInt();
375 1
            $containsEof = $this->readInt();
376
377 1
            if ($containsEof !== 0) {
378
                $iset->addOne(-1);
379
            }
380
381 1
            for ($j=0; $j < $n; $j++) {
382 1
                $i1 = $readUnicode();
383 1
                $i2 = $readUnicode();
384 1
                $iset->addRange($i1, $i2);
385
            }
386
        }
387 1
    }
388
389
    /**
390
     * @param array<IntervalSet> $sets
391
     */
392 1
    private function readEdges(ATN $atn, array &$sets) : void
393
    {
394 1
        $nEdges = $this->readInt();
395
396 1
        for ($i=0; $i < $nEdges; $i++) {
397 1
            $src = $this->readInt();
398 1
            $trg = $this->readInt();
399 1
            $ttype = $this->readInt();
400 1
            $arg1 = $this->readInt();
401 1
            $arg2 = $this->readInt();
402 1
            $arg3 = $this->readInt();
403 1
            $trans = $this->edgeFactory($atn, $ttype, $src, $trg, $arg1, $arg2, $arg3, $sets);
404 1
            $srcState = $atn->states[$src];
405 1
            $srcState->addTransition($trans);
406
        }
407
408
        // edges for rule stop states can be derived, so they aren't serialized
409 1
        foreach ($atn->states as $state) {
410 1
            foreach ($state->getTransitions() as $t) {
411 1
                if (!$t instanceof RuleTransition) {
412 1
                    continue;
413
                }
414
415 1
                $outermostPrecedenceReturn = -1;
416 1
                if ($atn->ruleToStartState[$t->target->ruleIndex]->isLeftRecursiveRule) {
417 1
                    if ($t->precedence === 0) {
418 1
                        $outermostPrecedenceReturn = $t->target->ruleIndex;
419
                    }
420
                }
421
422 1
                $trans = new EpsilonTransition($t->followState, $outermostPrecedenceReturn);
423 1
                $atn->ruleToStopState[$t->target->ruleIndex]->addTransition($trans);
424
            }
425
        }
426
427 1
        foreach ($atn->states as $state) {
428 1
            if ($state instanceof BlockStartState) {
429
                // we need to know the end state to set its start state
430 1
                if ($state->endState === null) {
431
                    throw new \RuntimeException('Unexpected null EndState.');
432
                }
433
434
                // block end states can only be associated to a single block start state
435 1
                if ($state->endState->startState !== null) {
436
                    throw new \RuntimeException('Unexpected null StartState.');
437
                }
438
439 1
                $state->endState->startState = $state;
440
            }
441
442 1
            if ($state instanceof PlusLoopbackState) {
443 1
                foreach ($state->getTransitions() as $t) {
444 1
                    $target = $t->target;
445
446 1
                    if ($target instanceof PlusBlockStartState) {
447 1
                        $target->loopBackState = $state;
448
                    }
449
                }
450 1
            } elseif ($state instanceof StarLoopbackState) {
451 1
                foreach ($state->getTransitions() as $t) {
452 1
                    $target = $t->target;
453
454 1
                    if ($target instanceof StarLoopEntryState) {
455 1
                        $target->loopBackState = $state;
456
                    }
457
                }
458
            }
459
        }
460 1
    }
461
462 1
    private function readDecisions(ATN $atn) : void
463
    {
464 1
        $decisions = $this->readInt();
465
466 1
        for ($i = 0; $i < $decisions; $i++) {
467 1
            $s = $this->readInt();
468
            /** @var DecisionState $decState */
469 1
            $decState = $atn->states[$s];
470
471 1
            $atn->decisionToState[] = $decState;
472
473 1
            $decState->decision = $i;
474
        }
475 1
    }
476
477 1
    private function readLexerActions(ATN $atn) : void
478
    {
479 1
        if ($atn->grammarType === ATN::ATN_TYPE_LEXER) {
480 1
            $count = $this->readInt();
481
482 1
            $atn->lexerActions = [];
483 1
            for ($i = 0; $i < $count; $i++) {
484 1
                $actionType = $this->readInt();
485 1
                $data1 = $this->readInt();
486
487 1
                if ($data1 === 0xFFFF) {
488
                    $data1 = -1;
489
                }
490
491 1
                $data2 = $this->readInt();
492
493 1
                if ($data2 === 0xFFFF) {
494
                    $data2 = -1;
495
                }
496
497 1
                $lexerAction = $this->lexerActionFactory($actionType, $data1, $data2);
498 1
                $atn->lexerActions[$i] = $lexerAction;
499
            }
500
        }
501 1
    }
502
503
    private function generateRuleBypassTransitions(ATN $atn) : void
504
    {
505
        $count = \count($atn->ruleToStartState);
506
507
        for ($i = 0; $i < $count; $i++) {
508
            $atn->ruleToTokenType[$i] = $atn->maxTokenType + $i + 1;
509
        }
510
511
        for ($i = 0; $i < $count; $i++) {
512
            $this->generateRuleBypassTransition($atn, $i);
513
        }
514
    }
515
516
    private function generateRuleBypassTransition(ATN $atn, int $idx) : void
517
    {
518
        $bypassStart = new BasicBlockStartState();
519
        $bypassStart->ruleIndex = $idx;
520
        $atn->addState($bypassStart);
521
522
        $bypassStop = new BlockEndState();
523
        $bypassStop->ruleIndex = $idx;
524
        $atn->addState($bypassStop);
525
526
        $bypassStart->endState = $bypassStop;
527
        $atn->defineDecisionState($bypassStart);
528
529
        $bypassStop->startState = $bypassStart;
530
531
        $excludeTransition = null;
532
        if ($atn->ruleToStartState[$idx]->isLeftRecursiveRule) {
533
            // wrap from the beginning of the rule to the StarLoopEntryState
534
            $endState = null;
535
536
            foreach ($atn->states as $state) {
537
                if ($this->stateIsEndStateFor($state, $idx)) {
538
                    $endState = $state;
539
540
                    if (!$state instanceof LoopEndState) {
541
                        throw new \RuntimeException('Unexpected state type.');
542
                    }
543
544
                    if ($state->loopBackState === null) {
545
                        throw new \RuntimeException('Unexpected null loop back state.');
546
                    }
547
548
                    $excludeTransition = $state->loopBackState->getTransition(0);
549
550
                    break;
551
                }
552
            }
553
554
            if ($excludeTransition === null) {
555
                throw new \RuntimeException('Couldn\'t identify final state of the precedence rule prefix section.');
556
            }
557
        } else {
558
            $endState = $atn->ruleToStopState[$idx];
559
        }
560
561
        // all non-excluded transitions that currently target end state need to target blockEnd instead
562
        // TODO:looks like a bug
563
        foreach ($atn->states as $state) {
564
            foreach ($state->getTransitions() as $transition) {
565
                if ($excludeTransition !== null && $transition->equals($excludeTransition)) {
566
                    continue;
567
                }
568
569
                if ($endState !== null && $transition->target->equals($endState)) {
570
                    $transition->target = $bypassStop;
571
                }
572
            }
573
        }
574
575
        // all transitions leaving the rule start state need to leave blockStart instead
576
        $ruleToStartState = $atn->ruleToStartState[$idx];
577
        $count = $ruleToStartState->getNumberOfTransitions();
578
579
        while ($count > 0) {
580
            $bypassStart->addTransition($ruleToStartState->getTransition($count-1));
581
            $ruleToStartState->setTransitions(\array_slice($ruleToStartState->getTransitions(), -1));
582
        }
583
584
        // link the new states
585
        $atn->ruleToStartState[$idx]->addTransition(new EpsilonTransition($bypassStart));
586
587
        if ($endState === null) {
588
            throw new \RuntimeException('Unexpected null end state.');
589
        }
590
591
        $bypassStop->addTransition(new EpsilonTransition($endState));
592
593
        $matchState = new BasicState();
594
        $atn->addState($matchState);
595
        $matchState->addTransition(new AtomTransition($bypassStop, $atn->ruleToTokenType[$idx] ?? 0));
596
        $bypassStart->addTransition(new EpsilonTransition($matchState));
597
    }
598
599
    private function stateIsEndStateFor(ATNState $state, int $idx) : ?ATNState
600
    {
601
        if ($state->ruleIndex !== $idx) {
602
            return null;
603
        }
604
605
        if (!$state instanceof StarLoopEntryState) {
606
            return null;
607
        }
608
609
        $maybeLoopEndState = $state->getTransition($state->getNumberOfTransitions() - 1)->target;
610
611
        if (!$maybeLoopEndState instanceof LoopEndState) {
612
            return null;
613
        }
614
615
        if ($maybeLoopEndState->epsilonOnlyTransitions
616
            && $maybeLoopEndState->getTransition(0)->target instanceof RuleStopState) {
617
            return $state;
618
        }
619
620
        return null;
621
    }
622
623
    /**
624
     * Analyze the {@see StarLoopEntryState} states in the specified ATN to set
625
     * the {@see StarLoopEntryState::$isPrecedenceDecision} field to the correct
626
     * value.
627
     *
628
     * @param ATN $atn The ATN.
629
     */
630 1
    private function markPrecedenceDecisions(ATN $atn) : void
631
    {
632 1
        foreach ($atn->states as $state) {
633 1
            if (!$state instanceof StarLoopEntryState) {
634 1
                continue;
635
            }
636
637
            // We analyze the ATN to determine if this ATN decision state is the
638
            // decision for the closure block that determines whether a
639
            // precedence rule should continue or complete.
640 1
            if ($atn->ruleToStartState[$state->ruleIndex]->isLeftRecursiveRule) {
641 1
                $maybeLoopEndState = $state->getTransition($state->getNumberOfTransitions() - 1)->target;
642
643 1
                if ($maybeLoopEndState instanceof LoopEndState) {
644 1
                    if ($maybeLoopEndState->epsilonOnlyTransitions
645 1
                        && $maybeLoopEndState->getTransition(0)->target instanceof RuleStopState) {
646 1
                        $state->isPrecedenceDecision = true;
647
                    }
648
                }
649
            }
650
        }
651 1
    }
652
653 1
    private function verifyATN(ATN $atn) : void
654
    {
655 1
        if (!$this->deserializationOptions->isVerifyATN()) {
656
            return;
657
        }
658
659
        // verify assumptions
660 1
        foreach ($atn->states as $state) {
661 1
            $this->checkCondition($state->epsilonOnlyTransitions || $state->getNumberOfTransitions() <= 1);
662
663
            switch (true) {
664 1
                case $state instanceof PlusBlockStartState:
665 1
                    $this->checkCondition($state->loopBackState !== null);
666
667 1
                    break;
668
669 1
                case $state instanceof StarLoopEntryState:
670 1
                    $this->checkCondition($state->loopBackState !== null);
671 1
                    $this->checkCondition($state->getNumberOfTransitions() === 2);
672
673 1
                    if ($state->getTransition(0)->target instanceof StarBlockStartState) {
674 1
                        $this->checkCondition($state->getTransition(1)->target instanceof LoopEndState);
675 1
                        $this->checkCondition(!$state->nonGreedy);
676
                    } elseif ($state->getTransition(0)->target instanceof LoopEndState) {
677
                        $this->checkCondition($state->getTransition(1)->target instanceof StarBlockStartState);
678
                        $this->checkCondition($state->nonGreedy);
679
                    } else {
680
                        throw new \InvalidArgumentException('IllegalState');
681
                    }
682
683 1
                    break;
684
685 1
                case $state instanceof StarLoopbackState:
686 1
                    $this->checkCondition($state->getNumberOfTransitions() === 1);
687 1
                    $this->checkCondition($state->getTransition(0)->target instanceof StarLoopEntryState);
688
689 1
                    break;
690
691 1
                case $state instanceof LoopEndState:
692 1
                    $this->checkCondition($state->loopBackState !== null);
693
694 1
                    break;
695
696 1
                case $state instanceof RuleStartState:
697 1
                    $this->checkCondition($state->stopState !== null);
698
699 1
                    break;
700
701 1
                case $state instanceof BlockStartState:
702 1
                    $this->checkCondition($state->endState !== null);
703
704 1
                    break;
705
706 1
                case $state instanceof BlockEndState:
707 1
                    $this->checkCondition($state->startState !== null);
708
709 1
                    break;
710
711 1
                case $state instanceof DecisionState:
712 1
                    $this->checkCondition($state->getNumberOfTransitions() <= 1 || $state->decision >= 0);
713
714 1
                    break;
715
716
                default:
717 1
                    $this->checkCondition($state->getNumberOfTransitions() <= 1 || $state instanceof RuleStopState);
718
            }
719
        }
720 1
    }
721
722 1
    private function checkCondition(?bool $condition, $message = 'IllegalState') : void
723
    {
724 1
        if ($condition === null) {
725
            throw new \InvalidArgumentException($message);
726
        }
727 1
    }
728
729 1
    private function readInt() : int
730
    {
731 1
        return $this->data[$this->pos++];
732
    }
733
734
    private function readInt32() : int
735
    {
736
        $low = $this->readInt();
737
        $high = $this->readInt();
738
739
        return $low | ($high << 16);
740
    }
741
742 1
    private function readUUID() : string
743
    {
744 1
        $bb = [];
745 1
        for ($i=0; $i < 8; $i++) {
746 1
            $int = $this->readInt();
747 1
            $bb[] = $int & 0xFF;
748 1
            $bb[] = ($int >> 8) & 0xFF;
749
        }
750
751 1
        $bb = \array_reverse($bb);
752 1
        $hex = \strtoupper(\bin2hex(\implode(\array_map('chr', $bb))));
753
754 1
        return \vsprintf('%s%s-%s-%s-%s-%s%s%s', \str_split($hex, 4));
755
    }
756
757
    /**
758
     * @param array<IntervalSet> $sets
759
     */
760 1
    private function edgeFactory(
761
        ATN $atn,
762
        int $type,
763
        int $src,
0 ignored issues
show
Unused Code introduced by
The parameter $src is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

763
        /** @scrutinizer ignore-unused */ int $src,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
764
        int $trg,
765
        int $arg1,
766
        int $arg2,
767
        int $arg3,
768
        array $sets
769
    ) : Transition {
770 1
        $target = $atn->states[$trg];
771
772
        switch ($type) {
773 1
            case Transition::EPSILON:
774 1
                return new EpsilonTransition($target);
775
776 1
            case Transition::RANGE:
777
                return $arg3 !== 0 ?
778
                    new RangeTransition($target, Token::EOF, $arg2) :
779
                    new RangeTransition($target, $arg1, $arg2);
780
781 1
            case Transition::RULE:
782 1
                $ruleStart = $atn->states[$arg1];
783
784 1
                if (!$ruleStart instanceof RuleStartState) {
785
                    throw new \RuntimeException('Unexpected transition type.');
786
                }
787
788 1
                return new RuleTransition($ruleStart, $arg2, $arg3, $target);
789
790 1
            case Transition::PREDICATE:
791
                return new PredicateTransition($target, $arg1, $arg2, $arg3 !== 0);
792
793 1
            case Transition::PRECEDENCE:
794 1
                return new PrecedencePredicateTransition($target, $arg1);
795
796 1
            case Transition::ATOM:
797 1
                return $arg3 !== 0 ? new AtomTransition($target, Token::EOF) : new AtomTransition($target, $arg1);
798
799 1
            case Transition::ACTION:
800 1
                return new ActionTransition($target, $arg1, $arg2, $arg3 !== 0);
801
802 1
            case Transition::SET:
803 1
                return new SetTransition($target, $sets[$arg1]);
804
805
            case Transition::NOT_SET:
806
                return new NotSetTransition($target, $sets[$arg1]);
807
808
            case Transition::WILDCARD:
809
                return new WildcardTransition($target);
810
811
            default:
812
                throw new \InvalidArgumentException(\sprintf(
813
                    'The specified transition type: %d is not valid.',
814
                    $type
815
                ));
816
        }
817
    }
818
819 1
    private function stateFactory(int $type, int $ruleIndex) : ?ATNState
820
    {
821
        switch ($type) {
822 1
            case ATNState::INVALID_TYPE:
823
                return null;
824
825 1
            case ATNState::BASIC:
826 1
                $s = new BasicState();
827
828 1
                break;
829
830 1
            case ATNState::RULE_START:
831 1
                $s = new RuleStartState();
832
833 1
                break;
834
835 1
            case ATNState::BLOCK_START:
836 1
                $s = new BasicBlockStartState();
837
838 1
                break;
839
840 1
            case ATNState::PLUS_BLOCK_START:
841 1
                $s = new PlusBlockStartState();
842
843 1
                break;
844
845 1
            case ATNState::STAR_BLOCK_START:
846 1
                $s = new StarBlockStartState();
847
848 1
                break;
849
850 1
            case ATNState::TOKEN_START:
851 1
                $s = new TokensStartState();
852
853 1
                break;
854
855 1
            case ATNState::RULE_STOP:
856 1
                $s = new RuleStopState();
857
858 1
                break;
859
860 1
            case ATNState::BLOCK_END:
861 1
                $s = new BlockEndState();
862
863 1
                break;
864
865 1
            case ATNState::STAR_LOOP_BACK:
866 1
                $s = new StarLoopbackState();
867
868 1
                break;
869
870 1
            case ATNState::STAR_LOOP_ENTRY:
871 1
                $s = new StarLoopEntryState();
872
873 1
                break;
874
875 1
            case ATNState::PLUS_LOOP_BACK:
876 1
                $s = new PlusLoopbackState();
877
878 1
                break;
879
880 1
            case ATNState::LOOP_END:
881 1
                $s = new LoopEndState();
882
883 1
                break;
884
885
            default:
886
                throw new \InvalidArgumentException(\sprintf(
887
                    'The specified state type %d is not valid.',
888
                    $type
889
                ));
890
        }
891
892 1
        $s->ruleIndex = $ruleIndex;
893
894 1
        return $s;
895
    }
896
897 1
    private function lexerActionFactory(int $type, int $data1, int $data2) : LexerAction
898
    {
899
        switch ($type) {
900 1
            case LexerActionType::CHANNEL:
901
                return new LexerChannelAction($data1);
902
903 1
            case LexerActionType::CUSTOM:
904
                return new LexerCustomAction($data1, $data2);
905
906 1
            case LexerActionType::MODE:
907
                return new LexerModeAction($data1);
908
909 1
            case LexerActionType::MORE:
910
                return LexerMoreAction::instance();
911
912 1
            case LexerActionType::POP_MODE:
913
                return LexerPopModeAction::instance();
914
915 1
            case LexerActionType::PUSH_MODE:
916
                return new LexerPushModeAction($data1);
917
918 1
            case LexerActionType::SKIP:
919 1
                return LexerSkipAction::instance();
920
921
            case LexerActionType::TYPE:
922
                return new LexerTypeAction($data1);
923
924
            default:
925
                throw new \InvalidArgumentException(\sprintf(
926
                    'The specified lexer action type %d is not valid.',
927
                    $type
928
                ));
929
        }
930
    }
931
}
932