1<?php
2
3declare(strict_types=1);
4
5namespace Antlr\Antlr4\Runtime\Atn;
6
7use Antlr\Antlr4\Runtime\Atn\Actions\LexerAction;
8use Antlr\Antlr4\Runtime\Atn\Actions\LexerActionType;
9use Antlr\Antlr4\Runtime\Atn\Actions\LexerChannelAction;
10use Antlr\Antlr4\Runtime\Atn\Actions\LexerCustomAction;
11use Antlr\Antlr4\Runtime\Atn\Actions\LexerModeAction;
12use Antlr\Antlr4\Runtime\Atn\Actions\LexerMoreAction;
13use Antlr\Antlr4\Runtime\Atn\Actions\LexerPopModeAction;
14use Antlr\Antlr4\Runtime\Atn\Actions\LexerPushModeAction;
15use Antlr\Antlr4\Runtime\Atn\Actions\LexerSkipAction;
16use Antlr\Antlr4\Runtime\Atn\Actions\LexerTypeAction;
17use Antlr\Antlr4\Runtime\Atn\States\ATNState;
18use Antlr\Antlr4\Runtime\Atn\States\BasicBlockStartState;
19use Antlr\Antlr4\Runtime\Atn\States\BasicState;
20use Antlr\Antlr4\Runtime\Atn\States\BlockEndState;
21use Antlr\Antlr4\Runtime\Atn\States\BlockStartState;
22use Antlr\Antlr4\Runtime\Atn\States\DecisionState;
23use Antlr\Antlr4\Runtime\Atn\States\LoopEndState;
24use Antlr\Antlr4\Runtime\Atn\States\PlusBlockStartState;
25use Antlr\Antlr4\Runtime\Atn\States\PlusLoopbackState;
26use Antlr\Antlr4\Runtime\Atn\States\RuleStartState;
27use Antlr\Antlr4\Runtime\Atn\States\RuleStopState;
28use Antlr\Antlr4\Runtime\Atn\States\StarBlockStartState;
29use Antlr\Antlr4\Runtime\Atn\States\StarLoopbackState;
30use Antlr\Antlr4\Runtime\Atn\States\StarLoopEntryState;
31use Antlr\Antlr4\Runtime\Atn\States\TokensStartState;
32use Antlr\Antlr4\Runtime\Atn\Transitions\ActionTransition;
33use Antlr\Antlr4\Runtime\Atn\Transitions\AtomTransition;
34use Antlr\Antlr4\Runtime\Atn\Transitions\EpsilonTransition;
35use Antlr\Antlr4\Runtime\Atn\Transitions\NotSetTransition;
36use Antlr\Antlr4\Runtime\Atn\Transitions\PrecedencePredicateTransition;
37use Antlr\Antlr4\Runtime\Atn\Transitions\PredicateTransition;
38use Antlr\Antlr4\Runtime\Atn\Transitions\RangeTransition;
39use Antlr\Antlr4\Runtime\Atn\Transitions\RuleTransition;
40use Antlr\Antlr4\Runtime\Atn\Transitions\SetTransition;
41use Antlr\Antlr4\Runtime\Atn\Transitions\Transition;
42use Antlr\Antlr4\Runtime\Atn\Transitions\WildcardTransition;
43use Antlr\Antlr4\Runtime\IntervalSet;
44use Antlr\Antlr4\Runtime\Token;
45use Antlr\Antlr4\Runtime\Utils\StringUtils;
46
47final 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;
96
97    /** @var array<int, callable|null>|null */
98    private $actionFactories;
99
100    public function __construct(?ATNDeserializationOptions $options = null)
101    {
102        $this->deserializationOptions = $options ?? ATNDeserializationOptions::defaultOptions();
103    }
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    protected function isFeatureSupported(string $feature, string $actualUuid) : bool
120    {
121        $featureIndex = \array_search($feature, self::SUPPORTED_UUIDS, true);
122
123        if ($featureIndex === false) {
124            return false;
125        }
126
127        $actualUuidIndex = \array_search($actualUuid, self::SUPPORTED_UUIDS, true);
128
129        return $actualUuidIndex >= $featureIndex;
130    }
131
132    public function deserialize(string $data) : ATN
133    {
134        $this->reset($data);
135        $this->checkVersion();
136        $this->checkUUID();
137        $atn = $this->readATN();
138        $this->readStates($atn);
139        $this->readRules($atn);
140        $this->readModes($atn);
141        $sets = [];
142
143        // First, deserialize sets with 16-bit arguments <= U+FFFF.
144        $this->readSets($sets, function () {
145            return $this->readInt();
146        });
147
148        // Next, if the ATN was serialized with the Unicode SMP feature,
149        // deserialize sets with 32-bit arguments <= U+10FFFF.
150
151        if ($this->isFeatureSupported(self::ADDED_UNICODE_SMP, $this->uuid)) {
152            $this->readSets($sets, function () {
153                return $this->readInt32();
154            });
155        }
156
157        $this->readEdges($atn, $sets);
158        $this->readDecisions($atn);
159        $this->readLexerActions($atn);
160        $this->markPrecedenceDecisions($atn);
161        $this->verifyATN($atn);
162
163        if ($atn->grammarType === ATN::ATN_TYPE_PARSER
164            && $this->deserializationOptions->isGenerateRuleBypassTransitions()) {
165            $this->generateRuleBypassTransitions($atn);
166            // re-verify after modification
167            $this->verifyATN($atn);
168        }
169
170        return $atn;
171    }
172
173    private function reset(string $data) : void
174    {
175        $characters = \preg_split('//u', $data, -1, \PREG_SPLIT_NO_EMPTY);
176
177        if ($characters === false) {
178            return;
179        }
180
181        $this->data = [StringUtils::codePoint($characters[0])];
182        for ($i = 1, $length = \count($characters); $i < $length; $i++) {
183            $code = StringUtils::codePoint($characters[$i]);
184            $this->data[] = $code > 1  ? $code - 2 : $code + 65533;
185        }
186
187        $this->pos = 0;
188    }
189
190
191    private function checkVersion() : void
192    {
193        $version = $this->readInt();
194
195        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    }
203
204    private function checkUUID() : void
205    {
206        $uuid = $this->readUUID();
207
208        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        $this->uuid = $uuid;
217    }
218
219    private function readATN() : ATN
220    {
221        $grammarType = $this->readInt();
222        $maxTokenType = $this->readInt();
223
224        return new ATN($grammarType, $maxTokenType);
225    }
226
227    private function readStates(ATN $atn) : void
228    {
229        $loopBackStateNumbers = [];
230        $endStateNumbers = [];
231        $nstates = $this->readInt();
232
233        for ($i=0; $i < $nstates; $i++) {
234            $stype = $this->readInt();
235
236            // ignore bad type of states
237            if ($stype === ATNState::INVALID_TYPE) {
238                $atn->addState(null);
239
240                continue;
241            }
242
243            $ruleIndex = $this->readInt();
244
245            if ($ruleIndex === 0xFFFF) {
246                $ruleIndex = -1;
247            }
248
249            $s = $this->stateFactory($stype, $ruleIndex);
250
251            if ($stype === ATNState::LOOP_END) {
252                // special case
253                $loopBackStateNumber = $this->readInt();
254
255                if (!$s instanceof LoopEndState) {
256                    throw new \RuntimeException('Unexpected ATN State');
257                }
258
259                $loopBackStateNumbers[] = [$s, $loopBackStateNumber];
260            } elseif ($s instanceof BlockStartState) {
261                $endStateNumber = $this->readInt();
262
263                $endStateNumbers[] = [$s, $endStateNumber];
264            }
265
266            $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        foreach ($loopBackStateNumbers as $pair) {
272            $pair[0]->loopBackState = $atn->states[$pair[1]];
273        }
274
275        foreach ($endStateNumbers as $pair) {
276            $endState = $atn->states[$pair[1]];
277
278            if (!$endState instanceof BlockEndState) {
279                throw new \RuntimeException('Unexpected ATN State');
280            }
281
282            $pair[0]->endState = $endState;
283        }
284
285        $numNonGreedyStates = $this->readInt();
286
287        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        $numPrecedenceStates = $this->readInt();
298
299        for ($j=0; $j < $numPrecedenceStates; $j++) {
300            $ruleStartState = $atn->states[$this->readInt()];
301
302            if (!$ruleStartState instanceof RuleStartState) {
303                throw new \RuntimeException('Unexpected ATN State');
304            }
305
306            $ruleStartState->isLeftRecursiveRule = true;
307        }
308    }
309
310    private function readRules(ATN $atn) : void
311    {
312        $nRules = $this->readInt();
313
314        $atn->ruleToTokenType = [];
315        $atn->ruleToStartState = [];
316        for ($i=0; $i < $nRules; $i++) {
317            $s = $this->readInt();
318            $startState = $atn->states[$s];
319
320            if (!$startState instanceof RuleStartState) {
321                throw new \RuntimeException('Unexpected ATN State');
322            }
323
324            $atn->ruleToStartState[$i] = $startState;
325
326            if ($atn->grammarType === ATN::ATN_TYPE_LEXER) {
327                $tokenType = $this->readInt();
328
329                if ($tokenType === 0xFFFF) {
330                    $tokenType = Token::EOF;
331                }
332
333                $atn->ruleToTokenType[$i] = $tokenType;
334            }
335        }
336
337        $atn->ruleToStopState = [];
338        foreach ($atn->states as $state) {
339            if (!$state instanceof RuleStopState) {
340                continue;
341            }
342
343            $atn->ruleToStopState[$state->ruleIndex] = $state;
344            $atn->ruleToStartState[$state->ruleIndex]->stopState = $state;
345        }
346    }
347
348    private function readModes(ATN $atn) : void
349    {
350        $nmodes = $this->readInt();
351
352        for ($i=0; $i < $nmodes; $i++) {
353            $tokensStartState = $atn->states[$this->readInt()];
354
355            if (!$tokensStartState instanceof TokensStartState) {
356                throw new \RuntimeException('Unexpected ATN State');
357            }
358
359            $atn->modeToStartState[] = $tokensStartState;
360        }
361    }
362
363    /**
364     * @param array<IntervalSet> $sets
365     */
366    private function readSets(array &$sets, callable $readUnicode) : void
367    {
368        $m = $this->readInt();
369
370        for ($i=0; $i < $m; $i++) {
371            $iset = new IntervalSet();
372
373            $sets[] = $iset;
374            $n = $this->readInt();
375            $containsEof = $this->readInt();
376
377            if ($containsEof !== 0) {
378                $iset->addOne(-1);
379            }
380
381            for ($j=0; $j < $n; $j++) {
382                $i1 = $readUnicode();
383                $i2 = $readUnicode();
384                $iset->addRange($i1, $i2);
385            }
386        }
387    }
388
389    /**
390     * @param array<IntervalSet> $sets
391     */
392    private function readEdges(ATN $atn, array &$sets) : void
393    {
394        $nEdges = $this->readInt();
395
396        for ($i=0; $i < $nEdges; $i++) {
397            $src = $this->readInt();
398            $trg = $this->readInt();
399            $ttype = $this->readInt();
400            $arg1 = $this->readInt();
401            $arg2 = $this->readInt();
402            $arg3 = $this->readInt();
403            $trans = $this->edgeFactory($atn, $ttype, $src, $trg, $arg1, $arg2, $arg3, $sets);
404            $srcState = $atn->states[$src];
405            $srcState->addTransition($trans);
406        }
407
408        // edges for rule stop states can be derived, so they aren't serialized
409        foreach ($atn->states as $state) {
410            foreach ($state->getTransitions() as $t) {
411                if (!$t instanceof RuleTransition) {
412                    continue;
413                }
414
415                $outermostPrecedenceReturn = -1;
416                if ($atn->ruleToStartState[$t->target->ruleIndex]->isLeftRecursiveRule) {
417                    if ($t->precedence === 0) {
418                        $outermostPrecedenceReturn = $t->target->ruleIndex;
419                    }
420                }
421
422                $trans = new EpsilonTransition($t->followState, $outermostPrecedenceReturn);
423                $atn->ruleToStopState[$t->target->ruleIndex]->addTransition($trans);
424            }
425        }
426
427        foreach ($atn->states as $state) {
428            if ($state instanceof BlockStartState) {
429                // we need to know the end state to set its start state
430                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                if ($state->endState->startState !== null) {
436                    throw new \RuntimeException('Unexpected null StartState.');
437                }
438
439                $state->endState->startState = $state;
440            }
441
442            if ($state instanceof PlusLoopbackState) {
443                foreach ($state->getTransitions() as $t) {
444                    $target = $t->target;
445
446                    if ($target instanceof PlusBlockStartState) {
447                        $target->loopBackState = $state;
448                    }
449                }
450            } elseif ($state instanceof StarLoopbackState) {
451                foreach ($state->getTransitions() as $t) {
452                    $target = $t->target;
453
454                    if ($target instanceof StarLoopEntryState) {
455                        $target->loopBackState = $state;
456                    }
457                }
458            }
459        }
460    }
461
462    private function readDecisions(ATN $atn) : void
463    {
464        $decisions = $this->readInt();
465
466        for ($i = 0; $i < $decisions; $i++) {
467            $s = $this->readInt();
468            /** @var DecisionState $decState */
469            $decState = $atn->states[$s];
470
471            $atn->decisionToState[] = $decState;
472
473            $decState->decision = $i;
474        }
475    }
476
477    private function readLexerActions(ATN $atn) : void
478    {
479        if ($atn->grammarType === ATN::ATN_TYPE_LEXER) {
480            $count = $this->readInt();
481
482            $atn->lexerActions = [];
483            for ($i = 0; $i < $count; $i++) {
484                $actionType = $this->readInt();
485                $data1 = $this->readInt();
486
487                if ($data1 === 0xFFFF) {
488                    $data1 = -1;
489                }
490
491                $data2 = $this->readInt();
492
493                if ($data2 === 0xFFFF) {
494                    $data2 = -1;
495                }
496
497                $lexerAction = $this->lexerActionFactory($actionType, $data1, $data2);
498                $atn->lexerActions[$i] = $lexerAction;
499            }
500        }
501    }
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    private function markPrecedenceDecisions(ATN $atn) : void
631    {
632        foreach ($atn->states as $state) {
633            if (!$state instanceof StarLoopEntryState) {
634                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            if ($atn->ruleToStartState[$state->ruleIndex]->isLeftRecursiveRule) {
641                $maybeLoopEndState = $state->getTransition($state->getNumberOfTransitions() - 1)->target;
642
643                if ($maybeLoopEndState instanceof LoopEndState) {
644                    if ($maybeLoopEndState->epsilonOnlyTransitions
645                        && $maybeLoopEndState->getTransition(0)->target instanceof RuleStopState) {
646                        $state->isPrecedenceDecision = true;
647                    }
648                }
649            }
650        }
651    }
652
653    private function verifyATN(ATN $atn) : void
654    {
655        if (!$this->deserializationOptions->isVerifyATN()) {
656            return;
657        }
658
659        // verify assumptions
660        foreach ($atn->states as $state) {
661            $this->checkCondition($state->epsilonOnlyTransitions || $state->getNumberOfTransitions() <= 1);
662
663            switch (true) {
664                case $state instanceof PlusBlockStartState:
665                    $this->checkCondition($state->loopBackState !== null);
666
667                    break;
668
669                case $state instanceof StarLoopEntryState:
670                    $this->checkCondition($state->loopBackState !== null);
671                    $this->checkCondition($state->getNumberOfTransitions() === 2);
672
673                    if ($state->getTransition(0)->target instanceof StarBlockStartState) {
674                        $this->checkCondition($state->getTransition(1)->target instanceof LoopEndState);
675                        $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                    break;
684
685                case $state instanceof StarLoopbackState:
686                    $this->checkCondition($state->getNumberOfTransitions() === 1);
687                    $this->checkCondition($state->getTransition(0)->target instanceof StarLoopEntryState);
688
689                    break;
690
691                case $state instanceof LoopEndState:
692                    $this->checkCondition($state->loopBackState !== null);
693
694                    break;
695
696                case $state instanceof RuleStartState:
697                    $this->checkCondition($state->stopState !== null);
698
699                    break;
700
701                case $state instanceof BlockStartState:
702                    $this->checkCondition($state->endState !== null);
703
704                    break;
705
706                case $state instanceof BlockEndState:
707                    $this->checkCondition($state->startState !== null);
708
709                    break;
710
711                case $state instanceof DecisionState:
712                    $this->checkCondition($state->getNumberOfTransitions() <= 1 || $state->decision >= 0);
713
714                    break;
715
716                default:
717                    $this->checkCondition($state->getNumberOfTransitions() <= 1 || $state instanceof RuleStopState);
718            }
719        }
720    }
721
722    private function checkCondition(?bool $condition, $message = 'IllegalState') : void
723    {
724        if ($condition === null) {
725            throw new \InvalidArgumentException($message);
726        }
727    }
728
729    private function readInt() : int
730    {
731        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    private function readUUID() : string
743    {
744        $bb = [];
745        for ($i=0; $i < 8; $i++) {
746            $int = $this->readInt();
747            $bb[] = $int & 0xFF;
748            $bb[] = ($int >> 8) & 0xFF;
749        }
750
751        $bb = \array_reverse($bb);
752        $hex = \strtoupper(\bin2hex(\implode(\array_map('chr', $bb))));
753
754        return \vsprintf('%s%s-%s-%s-%s-%s%s%s', \str_split($hex, 4));
755    }
756
757    /**
758     * @param array<IntervalSet> $sets
759     */
760    private function edgeFactory(
761        ATN $atn,
762        int $type,
763        int $src,
764        int $trg,
765        int $arg1,
766        int $arg2,
767        int $arg3,
768        array $sets
769    ) : Transition {
770        $target = $atn->states[$trg];
771
772        switch ($type) {
773            case Transition::EPSILON:
774                return new EpsilonTransition($target);
775
776            case Transition::RANGE:
777                return $arg3 !== 0 ?
778                    new RangeTransition($target, Token::EOF, $arg2) :
779                    new RangeTransition($target, $arg1, $arg2);
780
781            case Transition::RULE:
782                $ruleStart = $atn->states[$arg1];
783
784                if (!$ruleStart instanceof RuleStartState) {
785                    throw new \RuntimeException('Unexpected transition type.');
786                }
787
788                return new RuleTransition($ruleStart, $arg2, $arg3, $target);
789
790            case Transition::PREDICATE:
791                return new PredicateTransition($target, $arg1, $arg2, $arg3 !== 0);
792
793            case Transition::PRECEDENCE:
794                return new PrecedencePredicateTransition($target, $arg1);
795
796            case Transition::ATOM:
797                return $arg3 !== 0 ? new AtomTransition($target, Token::EOF) : new AtomTransition($target, $arg1);
798
799            case Transition::ACTION:
800                return new ActionTransition($target, $arg1, $arg2, $arg3 !== 0);
801
802            case Transition::SET:
803                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    private function stateFactory(int $type, int $ruleIndex) : ?ATNState
820    {
821        switch ($type) {
822            case ATNState::INVALID_TYPE:
823                return null;
824
825            case ATNState::BASIC:
826                $s = new BasicState();
827
828                break;
829
830            case ATNState::RULE_START:
831                $s = new RuleStartState();
832
833                break;
834
835            case ATNState::BLOCK_START:
836                $s = new BasicBlockStartState();
837
838                break;
839
840            case ATNState::PLUS_BLOCK_START:
841                $s = new PlusBlockStartState();
842
843                break;
844
845            case ATNState::STAR_BLOCK_START:
846                $s = new StarBlockStartState();
847
848                break;
849
850            case ATNState::TOKEN_START:
851                $s = new TokensStartState();
852
853                break;
854
855            case ATNState::RULE_STOP:
856                $s = new RuleStopState();
857
858                break;
859
860            case ATNState::BLOCK_END:
861                $s = new BlockEndState();
862
863                break;
864
865            case ATNState::STAR_LOOP_BACK:
866                $s = new StarLoopbackState();
867
868                break;
869
870            case ATNState::STAR_LOOP_ENTRY:
871                $s = new StarLoopEntryState();
872
873                break;
874
875            case ATNState::PLUS_LOOP_BACK:
876                $s = new PlusLoopbackState();
877
878                break;
879
880            case ATNState::LOOP_END:
881                $s = new LoopEndState();
882
883                break;
884
885            default:
886                throw new \InvalidArgumentException(\sprintf(
887                    'The specified state type %d is not valid.',
888                    $type
889                ));
890        }
891
892        $s->ruleIndex = $ruleIndex;
893
894        return $s;
895    }
896
897    private function lexerActionFactory(int $type, int $data1, int $data2) : LexerAction
898    {
899        switch ($type) {
900            case LexerActionType::CHANNEL:
901                return new LexerChannelAction($data1);
902
903            case LexerActionType::CUSTOM:
904                return new LexerCustomAction($data1, $data2);
905
906            case LexerActionType::MODE:
907                return new LexerModeAction($data1);
908
909            case LexerActionType::MORE:
910                return LexerMoreAction::instance();
911
912            case LexerActionType::POP_MODE:
913                return LexerPopModeAction::instance();
914
915            case LexerActionType::PUSH_MODE:
916                return new LexerPushModeAction($data1);
917
918            case LexerActionType::SKIP:
919                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