1<?php
2
3declare(strict_types=1);
4
5namespace Antlr\Antlr4\Runtime;
6
7use Antlr\Antlr4\Runtime\Atn\ATN;
8use Antlr\Antlr4\Runtime\Atn\ATNDeserializationOptions;
9use Antlr\Antlr4\Runtime\Atn\ATNDeserializer;
10use Antlr\Antlr4\Runtime\Atn\ParserATNSimulator;
11use Antlr\Antlr4\Runtime\Atn\States\ATNState;
12use Antlr\Antlr4\Runtime\Atn\Transitions\RuleTransition;
13use Antlr\Antlr4\Runtime\Dfa\DFA;
14use Antlr\Antlr4\Runtime\Error\ANTLRErrorStrategy;
15use Antlr\Antlr4\Runtime\Error\DefaultErrorStrategy;
16use Antlr\Antlr4\Runtime\Error\Exceptions\InputMismatchException;
17use Antlr\Antlr4\Runtime\Error\Exceptions\RecognitionException;
18use Antlr\Antlr4\Runtime\Tree\ErrorNode;
19use Antlr\Antlr4\Runtime\Tree\ErrorNodeImpl;
20use Antlr\Antlr4\Runtime\Tree\ParseTreeListener;
21use Antlr\Antlr4\Runtime\Tree\TerminalNode;
22use Antlr\Antlr4\Runtime\Tree\TerminalNodeImpl;
23
24/**
25 * This is all the parsing support code essentially; most of it is
26 * error recovery stuff.
27 */
28abstract class Parser extends Recognizer
29{
30    /**
31     * This field maps from the serialized ATN string to the deserialized
32     * {@see ATN} with bypass alternatives.
33     *
34     * @see ATNDeserializationOptions::isGenerateRuleBypassTransitions()
35     *
36     * @var array<string, ATN>
37     */
38    private static $bypassAltsAtnCache = [];
39
40    /**
41     * The error handling strategy for the parser. The default value is a new
42     * instance of {@see DefaultErrorStrategy}.
43     *
44     * @see Parser::getErrorHandler()
45     * @see Parser::setErrorHandler()
46     *
47     * @var ANTLRErrorStrategy
48     */
49    protected $errorHandler;
50
51    /**
52     * The input stream.
53     *
54     * @see Parser::getInputStream()
55     * @see Parser::setInputStream()
56     *
57     * @var TokenStream|null
58     */
59    protected $input;
60
61    /** @var array<int> */
62    protected $precedenceStack = [0];
63
64    /**
65     * The {@see ParserRuleContext} object for the currently executing rule.
66     * This is always non-null during the parsing process.
67     *
68     * @var ParserRuleContext|null
69     */
70    protected $ctx;
71
72    /**
73     * Specifies whether or not the parser should construct a parse tree during
74     * the parsing process. The default value is `true`.
75     *
76     * @see Parser::getBuildParseTree()
77     * @see Parser::setBuildParseTree()
78     *
79     * @var bool
80     */
81    protected $buildParseTree = true;
82
83    /**
84     * When {@see Parser::setTrace(true)} is called, a reference to the
85     * {@see TraceListener} is stored here so it can be easily removed in a
86     * later call to {@see Parser::setTrace(false)}. The listener itself is
87     * implemented as a parser listener so this field is not directly used by
88     * other parser methods.
89     *
90     * @var ParserTraceListener|null
91     */
92    private $tracer;
93
94    /**
95     * The list of {@see ParseTreeListener} listeners registered to receive
96     * events during the parse.
97     *
98     * @see Parser::addParseListener
99     *
100     * @var array<ParseTreeListener>
101     */
102    protected $parseListeners = [];
103
104    /**
105     * The number of syntax errors reported during parsing. This value is
106     * incremented each time {@see Parser::notifyErrorListeners()} is called.
107     *
108     * @var int
109     */
110    protected $syntaxErrors = 0;
111
112    /**
113     * Indicates parser has matched EOF token. See {@see Parser::exitRule()}.
114     *
115     * @var bool
116     */
117    protected $matchedEOF = false;
118
119    public function __construct(TokenStream $input)
120    {
121        parent::__construct();
122
123        $this->errorHandler = new DefaultErrorStrategy();
124
125        $this->setInputStream($input);
126    }
127
128    /** reset the parser's state */
129    public function reset() : void
130    {
131        if ($this->input !== null) {
132            $this->input->seek(0);
133        }
134
135        $this->errorHandler->reset($this);
136        $this->ctx = null;
137        $this->syntaxErrors = 0;
138        $this->matchedEOF = false;
139        $this->setTrace(false);
140        $this->precedenceStack = [0];
141
142        $interpreter = $this->getInterpreter();
143
144        if ($interpreter !== null) {
145            $interpreter->reset();
146        }
147    }
148
149    /**
150     * Match current input symbol against `ttype`. If the symbol type matches,
151     * {@see ANTLRErrorStrategy::reportMatch()} and {@see Parser::consume()}
152     * are called to complete the match process.
153     *
154     * If the symbol type does not match, {@see ANTLRErrorStrategy::recoverInline()}
155     * is called on the current error strategy to attempt recovery. If
156     * {@see Parser::getBuildParseTree()} is `true` and the token index
157     * of the symbol returned by {@see ANTLRErrorStrategy::recoverInline()}
158     * is -1, the symbol is added to the parse tree by calling
159     * {@see Parser::createErrorNode(ParserRuleContext, Token)} then
160     * {@see ParserRuleContext::addErrorNode(ErrorNode)}.
161     *
162     * @param int $ttype the token type to match.
163     *
164     * @return Token the matched symbol
165     *
166     * @throws InputMismatchException
167     * @throws RecognitionException If the current input symbol did not match
168     *                              and the error strategy could not recover
169     *                              from the mismatched symbol.
170     */
171    public function match(int $ttype) : Token
172    {
173        $t = $this->getCurrentToken();
174
175        if ($t !== null && $t->getType() === $ttype) {
176            if ($ttype === Token::EOF) {
177                $this->matchedEOF = true;
178            }
179
180            $this->errorHandler->reportMatch($this);
181
182            $this->consume();
183        } else {
184            $t = $this->errorHandler->recoverInline($this);
185
186            if ($this->buildParseTree && $t->getTokenIndex() === -1) {
187                // we must have conjured up a new token during single token insertion
188                // if it's not the current symbol
189                $this->context()->addErrorNode($this->createErrorNode($this->context(), $t));
190            }
191        }
192
193        return $t;
194    }
195
196    /**
197     * Match current input symbol as a wildcard. If the symbol type matches
198     * (i.e. has a value greater than 0), {@see ANTLRErrorStrategy::reportMatch()}
199     * and {@see Parser::consume()} are called to complete the match process.
200     *
201     * If the symbol type does not match, {@see ANTLRErrorStrategy::recoverInline()}
202     * is called on the current error strategy to attempt recovery.
203     * If {@see Parser::getBuildParseTree()} is `true` and the token index
204     * of the symbol returned by {@see ANTLRErrorStrategy::recoverInline()} is -1,
205     * the symbol is added to the parse tree by calling
206     * {@see Parser::createErrorNode(ParserRuleContext, Token)}. then
207     * {@see ParserRuleContext::addErrorNode(ErrorNode)}
208     *
209     * @return Token The matched symbol.
210     *
211     * @throws RecognitionException If the current input symbol did not match
212     *                              a wildcard and the error strategy could not
213     *                              recover from the mismatched symbol.
214     */
215    public function matchWildcard() : ?Token
216    {
217        $t = $this->token();
218
219        if ($t->getType() > 0) {
220            $this->errorHandler->reportMatch($this);
221            $this->consume();
222        } else {
223            $t = $this->errorHandler->recoverInline($this);
224
225            if ($this->buildParseTree && $t->getTokenIndex() === -1) {
226                // we must have conjured up a new token during single token insertion
227                // if it's not the current symbol
228                $this->context()->addErrorNode($this->createErrorNode($this->context(), $t));
229            }
230        }
231
232        return $t;
233    }
234
235    /**
236     * Track the {@see ParserRuleContext} objects during the parse and hook
237     * them up using the {@see ParserRuleContext::$children} list so that it
238     * forms a parse tree. The {@see ParserRuleContext} returned from the start
239     * rule represents the root of the parse tree.
240     *
241     * Note that if we are not building parse trees, rule contexts only point
242     * upwards. When a rule exits, it returns the context but that gets garbage
243     * collected if nobody holds a reference. It points upwards but nobody
244     * points at it.
245     *
246     * When we build parse trees, we are adding all of these contexts to
247     * {@see ParserRuleContext::$children} list. Contexts are then not
248     * candidates for garbage collection.
249     */
250    public function setBuildParseTree(bool $buildParseTree) : void
251    {
252        $this->buildParseTree = $buildParseTree;
253    }
254
255    /**
256     * Gets whether or not a complete parse tree will be constructed while
257     * parsing. This property is `true` for a newly constructed parser.
258     *
259     * @return bool `true` if a complete parse tree will be constructed while
260     *              parsing, otherwise `false`.
261     */
262    public function getBuildParseTree() : bool
263    {
264        return $this->buildParseTree;
265    }
266
267    /**
268     * @return array<ParseTreeListener>
269     */
270    public function getParseListeners() : array
271    {
272        return $this->parseListeners;
273    }
274
275    /**
276     * Registers `listener` to receive events during the parsing process.
277     *
278     * To support output-preserving grammar transformations (including but not
279     * limited to left-recursion removal, automated left-factoring, and
280     * optimized code generation), calls to listener methods during the parse
281     * may differ substantially from calls made by
282     * {@see ParseTreeWalker::DEFAULT} used after the parse is complete. In
283     * particular, rule entry and exit events may occur in a different order
284     * during the parse than after the parser. In addition, calls to certain
285     * rule entry methods may be omitted.
286     *
287     * With the following specific exceptions, calls to listener events are
288     * <em>deterministic</em>, i.e. for identical input the calls to listener
289     * methods will be the same.
290     *
291     * - Alterations to the grammar used to generate code may change the
292     * behavior of the listener calls.
293     * - Alterations to the command line options passed to ANTLR 4 when
294     * generating the parser may change the behavior of the listener calls.
295     * - Changing the version of the ANTLR Tool used to generate the parser
296     * may change the behavior of the listener calls.
297     *
298     * @param ParseTreeListener $listener The listener to add.
299     */
300    public function addParseListener(ParseTreeListener $listener) : void
301    {
302        if (!\in_array($listener, $this->parseListeners, true)) {
303            $this->parseListeners[] = $listener;
304        }
305    }
306
307    /**
308     * Remove `listener` from the list of parse listeners.
309     *
310     * If `listener` is `null` or has not been added as a parse
311     * listener, this method does nothing.
312     *
313     * @param ParseTreeListener $listener The listener to remove
314     *
315     * @see Parser::addParseListener()
316     */
317    public function removeParseListener(ParseTreeListener $listener) : void
318    {
319        $index = \array_search($listener, $this->parseListeners, true);
320
321        if ($index !== false) {
322            unset($this->parseListeners[$index]);
323        }
324    }
325
326    /**
327     * Remove all parse listeners.
328     *
329     * @see Parser::addParseListener()
330     */
331    public function removeParseListeners() : void
332    {
333        $this->parseListeners = [];
334    }
335
336    /**
337     * Notify any parse listeners of an enter rule event.
338     *
339     * @seeParser::addParseListener()
340     */
341    protected function triggerEnterRuleEvent() : void
342    {
343        foreach ($this->parseListeners as $listener) {
344            $listener->enterEveryRule($this->context());
345            $this->context()->enterRule($listener);
346        }
347    }
348
349    /**
350     * Notify any parse listeners of an exit rule event.
351     *
352     * @see Parser::addParseListener()
353     */
354    protected function triggerExitRuleEvent() : void
355    {
356        for ($i = \count($this->parseListeners) - 1; $i >= 0; $i--) {
357            /** @var ParseTreeListener $listener */
358            $listener = $this->parseListeners[$i];
359            $this->context()->exitRule($listener);
360            $listener->exitEveryRule($this->context());
361        }
362    }
363
364    /**
365     * Gets the number of syntax errors reported during parsing. This value is
366     * incremented each time {@see Parser::notifyErrorListeners()} is called.
367     *
368     * @see Parser::notifyErrorListeners()
369     */
370    public function getNumberOfSyntaxErrors() : int
371    {
372        return $this->syntaxErrors;
373    }
374
375    public function getTokenFactory() : TokenFactory
376    {
377        return $this->tokenStream()->getTokenSource()->getTokenFactory();
378    }
379
380    /**
381     * Tell our token source and error strategy about a new way to create tokens.
382     */
383    public function setTokenFactory(TokenFactory $factory) : void
384    {
385        $this->tokenStream()->getTokenSource()->setTokenFactory($factory);
386    }
387
388    /**
389     * The ATN with bypass alternatives is expensive to create so we create it
390     * lazily.
391     *
392     * @throws \RuntimeException If the current parser does not implement the
393     *                           {@see Parser::getSerializedATN()} method.
394     */
395    public function getATNWithBypassAlts() : ATN
396    {
397        $serializedAtn = $this->getSerializedATN();
398        $result = self::$bypassAltsAtnCache[$serializedAtn] ?? null;
399
400        if ($result === null) {
401            $deserializationOptions = new ATNDeserializationOptions();
402            $deserializationOptions->setGenerateRuleBypassTransitions(true);
403            $result = (new ATNDeserializer($deserializationOptions))->deserialize($serializedAtn);
404            self::$bypassAltsAtnCache[$serializedAtn] = $result;
405        }
406
407        return $result;
408    }
409
410    public function getErrorHandler() : ANTLRErrorStrategy
411    {
412        return $this->errorHandler;
413    }
414
415    public function setErrorHandler(ANTLRErrorStrategy $handler) : void
416    {
417        $this->errorHandler = $handler;
418    }
419
420    /**
421     * @return TokenStream|null
422     */
423    public function getInputStream() : ?IntStream
424    {
425        return $this->getTokenStream();
426    }
427
428    final public function setInputStream(IntStream $input) : void
429    {
430        if (!$input instanceof TokenStream) {
431            throw new \RuntimeException('The stream must be a token stream.');
432        }
433
434        $this->setTokenStream($input);
435    }
436
437    public function getTokenStream() : ?TokenStream
438    {
439        return $this->input;
440    }
441
442    private function tokenStream() : TokenStream
443    {
444        if ($this->input === null) {
445            throw new \RuntimeException('The current token stream is null.');
446        }
447
448        return $this->input;
449    }
450
451    /** Set the token stream and reset the parser. */
452    public function setTokenStream(TokenStream $input) : void
453    {
454        $this->input = null;
455        $this->reset();
456        $this->input = $input;
457    }
458
459    /**
460     * Match needs to return the current input symbol, which gets put
461     * into the label for the associated token ref; e.g., x=ID.
462     */
463    public function getCurrentToken() : ?Token
464    {
465        return $this->tokenStream()->LT(1);
466    }
467
468    private function token() : Token
469    {
470        $token = $this->getCurrentToken();
471
472        if ($token === null) {
473            throw new \RuntimeException('The current token is null.');
474        }
475
476        return $token;
477    }
478
479    final public function notifyErrorListeners(
480        string $msg,
481        ?Token $offendingToken = null,
482        ?RecognitionException $e = null
483    ) : void {
484        if ($offendingToken === null) {
485            $offendingToken = $this->token();
486        }
487
488        $this->syntaxErrors++;
489        $line = $offendingToken->getLine();
490        $charPositionInLine = $offendingToken->getCharPositionInLine();
491        $listener = $this->getErrorListenerDispatch();
492        $listener->syntaxError($this, $offendingToken, $line, $charPositionInLine, $msg, $e);
493    }
494
495    /**
496     * Consume and return the {@see Parser::getCurrentToken()} current symbol.
497     *
498     * E.g., given the following input with `A` being the current
499     * lookahead symbol, this function moves the cursor to `B` and returns
500     * `A`.
501     *
502     * <pre>
503     *  A B
504     *  ^
505     * </pre>
506     *
507     * If the parser is not in error recovery mode, the consumed symbol is added
508     * to the parse tree using {@see ParserRuleContext::addTerminalNode()}, and
509     * {@see ParseTreeListener::visitTerminal()} is called on any parse listeners.
510     * If the parser is in error recovery mode, the consumed symbol is
511     * added to the parse tree using {@see Parser::createErrorNode()} then
512     * {@see ParserRuleContext::addErrorNode()} and
513     * {@see ParseTreeListener::visitErrorNode()} is called on any parse
514     * listeners.
515     */
516    public function consume() : Token
517    {
518        $o = $this->token();
519
520        if ($o->getType() !== self::EOF) {
521            $this->tokenStream()->consume();
522        }
523
524        if ($this->buildParseTree || \count($this->parseListeners) > 0) {
525            if ($this->errorHandler->inErrorRecoveryMode($this)) {
526                $node = $this->context()->addErrorNode($this->createErrorNode($this->context(), $o));
527
528                foreach ($this->parseListeners as $listener) {
529                    if ($node instanceof ErrorNode) {
530                        $listener->visitErrorNode($node);
531                    }
532                }
533            } else {
534                $node = $this->context()->addTerminalNode($this->createTerminalNode($this->context(), $o));
535
536                foreach ($this->parseListeners as $listener) {
537                    if ($node instanceof TerminalNode) {
538                        $listener->visitTerminal($node);
539                    }
540                }
541            }
542        }
543
544        return $o;
545    }
546
547    /**
548     * How to create a token leaf node associated with a parent.
549     *
550     * Typically, the terminal node to create is not a function of the parent.
551     */
552    public function createTerminalNode(ParserRuleContext $parent, Token $t) : TerminalNode
553    {
554        return new TerminalNodeImpl($t);
555    }
556
557    /** How to create an error node, given a token, associated with a parent.
558     *  Typically, the error node to create is not a function of the parent.
559     *
560     * @since 4.7
561     */
562    public function createErrorNode(ParserRuleContext $parent, Token $t) : ErrorNode
563    {
564        return new ErrorNodeImpl($t);
565    }
566
567    protected function addContextToParseTree() : void
568    {
569        $parent = $this->context()->getParent();
570
571        if ($parent === null) {
572            return;
573        }
574
575        // add current context to parent if we have a parent
576        if ($parent instanceof ParserRuleContext) {
577            $parent->addChild($this->context());
578        }
579    }
580
581    /**
582     * Always called by generated parsers upon entry to a rule. Access field
583     * {@see Parser::$ctx} get the current context.
584     */
585    public function enterRule(ParserRuleContext $localctx, int $state, int $ruleIndex) : void
586    {
587        $this->setState($state);
588        $this->ctx = $localctx;
589        $this->context()->start = $this->tokenStream()->LT(1);
590
591        if ($this->buildParseTree) {
592            $this->addContextToParseTree();
593        }
594
595        $this->triggerEnterRuleEvent();
596    }
597
598    public function exitRule() : void
599    {
600        if ($this->matchedEOF) {
601            // if we have matched EOF, it cannot consume past EOF so we use LT(1) here
602            $this->context()->stop = $this->tokenStream()->LT(1); // LT(1) will be end of file
603        } else {
604            $this->context()->stop = $this->tokenStream()->LT(-1); // stop node is what we just matched
605        }
606
607        // trigger event on _ctx, before it reverts to parent
608        $this->triggerExitRuleEvent();
609
610        $this->setState($this->context()->invokingState);
611
612        $parent = $this->context()->getParent();
613
614        if ($parent === null || $parent instanceof ParserRuleContext) {
615            $this->ctx = $parent;
616        }
617    }
618
619    public function enterOuterAlt(ParserRuleContext $localctx, int $altNum) : void
620    {
621        $localctx->setAltNumber($altNum);
622
623        // if we have new localctx, make sure we replace existing ctx
624        // that is previous child of parse tree
625        if ($this->buildParseTree && $this->ctx !== $localctx) {
626            /** @var ParserRuleContext $parent */
627            $parent = $this->context()->getParent();
628
629            if ($parent !== null) {
630                $parent->removeLastChild();
631                $parent->addChild($localctx);
632            }
633        }
634
635        $this->ctx = $localctx;
636    }
637
638    /**
639     * Get the precedence level for the top-most precedence rule.
640     *
641     * @return int The precedence level for the top-most precedence rule, or -1
642     *             if the parser context is not nested within a precedence rule.
643     */
644    public function getPrecedence() : int
645    {
646        return $this->precedenceStack[\count($this->precedenceStack) - 1] ?? -1;
647    }
648
649    public function enterRecursionRule(ParserRuleContext $localctx, int $state, int $ruleIndex, int $precedence) : void
650    {
651        $this->setState($state);
652        $this->precedenceStack[] = $precedence;
653        $this->ctx = $localctx;
654        $this->context()->start = $this->tokenStream()->LT(1);
655
656        $this->triggerEnterRuleEvent(); // simulates rule entry for left-recursive rules
657    }
658
659    /**
660     * Like {@see Parser::enterRule()} but for recursive rules.
661     *
662     * Make the current context the child of the incoming `localctx`.
663     */
664    public function pushNewRecursionContext(ParserRuleContext $localctx, int $state, int $ruleIndex) : void
665    {
666        $previous = $this->context();
667        $previous->setParent($localctx);
668        $previous->invokingState = $state;
669        $previous->stop = $this->tokenStream()->LT(-1);
670
671        $this->ctx = $localctx;
672        $this->context()->start = $previous->start;
673
674        if ($this->buildParseTree) {
675            $this->context()->addChild($previous);
676        }
677
678        $this->triggerEnterRuleEvent(); // simulates rule entry for left-recursive rules
679    }
680
681    public function unrollRecursionContexts(?ParserRuleContext $parentctx) : void
682    {
683        \array_pop($this->precedenceStack);
684
685        $this->context()->stop = $this->tokenStream()->LT(-1);
686        $retctx = $this->context(); // save current ctx (return value)
687
688        // unroll so _ctx is as it was before call to recursive method
689
690        if (\count($this->parseListeners) > 0) {
691            while ($this->ctx !== $parentctx) {
692                $this->triggerExitRuleEvent();
693                $parent = $this->context()->getParent();
694
695                if ($parent !== null && !$parent instanceof ParserRuleContext) {
696                    throw new \RuntimeException('Unexpected context type.');
697                }
698
699                $this->ctx = $parent;
700            }
701        } else {
702            $this->ctx = $parentctx;
703        }
704
705        // hook into tree
706        $retctx->setParent($parentctx);
707
708        if ($this->buildParseTree && $parentctx !== null) {
709            // add return ctx into invoking rule's tree
710            $parentctx->addChild($retctx);
711        }
712    }
713
714    public function getInvokingContext(int $ruleIndex) : ?RuleContext
715    {
716        $p = $this->ctx;
717        while ($p !== null) {
718            if ($p->getRuleIndex() === $ruleIndex) {
719                return $p;
720            }
721
722            $p = $p->getParent();
723        }
724
725        return null;
726    }
727
728    public function getContext() : ?ParserRuleContext
729    {
730        return $this->ctx;
731    }
732
733    private function context() : ParserRuleContext
734    {
735        if ($this->ctx === null) {
736            throw new \RuntimeException('The current context is null.');
737        }
738
739        return $this->ctx;
740    }
741
742    public function getCurrentRuleName() : string
743    {
744        return $this->getRuleNames()[$this->context()->getRuleIndex()] ?? '';
745    }
746
747    public function setContext(ParserRuleContext $ctx) : void
748    {
749        $this->ctx = $ctx;
750    }
751
752    public function precpred(RuleContext $localctx, int $precedence) : bool
753    {
754        return $precedence >= $this->getPrecedence();
755    }
756
757    public function inContext(string $context) : bool
758    {
759        // TODO: useful in parser?
760        return false;
761    }
762
763    /**
764     * Checks whether or not `symbol` can follow the current state in the
765     * ATN. The behavior of this method is equivalent to the following, but is
766     * implemented such that the complete context-sensitive follow set does not
767     * need to be explicitly constructed.
768     *
769     * <pre>
770     * return getExpectedTokens().contains(symbol);
771     * </pre>
772     *
773     * @param int $symbol The symbol type to check
774     *
775     * @return bool `true` if `symbol` can follow the current state in
776     *              the ATN, otherwise `false`.
777     */
778    public function isExpectedToken(int $symbol) : bool
779    {
780        $atn = $this->interpreter()->atn;
781        /** @var ParserRuleContext $ctx */
782        $ctx = $this->ctx;
783        $s = $atn->states[$this->getState()];
784        $following = $atn->nextTokens($s);
785
786        if ($following->contains($symbol)) {
787            return true;
788        }
789
790        if (!$following->contains(Token::EPSILON)) {
791            return false;
792        }
793
794        while ($ctx !== null && $ctx->invokingState >= 0 && $following->contains(Token::EPSILON)) {
795            /** @var ATNState $invokingState */
796            $invokingState = $atn->states[$ctx->invokingState];
797            /** @var RuleTransition $rt */
798            $rt = $invokingState->getTransition(0);
799
800            $following = $atn->nextTokens($rt->followState);
801
802            if ($following->contains($symbol)) {
803                return true;
804            }
805
806            $ctx = $ctx->getParent();
807        }
808
809        return $following->contains(Token::EPSILON) && $symbol === Token::EOF;
810    }
811
812    public function isMatchedEOF() : bool
813    {
814        return $this->matchedEOF;
815    }
816
817    /**
818     * Computes the set of input symbols which could follow the current parser
819     * state and context, as given by {@see #getState} and {@see #getContext},
820     * respectively.
821     *
822     * @see ATN::getExpectedTokens()
823     */
824    public function getExpectedTokens() : IntervalSet
825    {
826        return $this->getATN()
827            ->getExpectedTokens($this->getState(), $this->getContext());
828    }
829
830    public function getExpectedTokensWithinCurrentRule() : IntervalSet
831    {
832        $atn = $this->interpreter()->atn;
833        $s = $atn->states[$this->getState()];
834
835        return $atn->nextTokens($s);
836    }
837
838    /** Get a rule's index (i.e., `RULE_ruleName` field) or -1 if not found. */
839    public function getRuleIndex(string $ruleName) : int
840    {
841        return $this->getRuleIndexMap()[$ruleName] ?? -1;
842    }
843
844    /**
845     * Return the string array of the rule names in your parser instance
846     * leading up to a call to the current rule. You could override if
847     * you want more details such as the file/line info of where
848     * in the ATN a rule is invoked.
849     *
850     * This is very useful for error messages.
851     *
852     * @return array<int, string>
853     */
854    public function getRuleInvocationStack(?RuleContext $p = null) : array
855    {
856        $p = $p ?? $this->ctx;
857        $ruleNames = $this->getRuleNames();
858        $stack = [];
859
860        while ($p !== null) {
861            // compute what follows who invoked us
862            $ruleIndex = $p->getRuleIndex();
863
864            if ($ruleIndex < 0) {
865                $stack[] = 'n/a';
866            } else {
867                $stack[] = $ruleNames[$ruleIndex];
868            }
869
870            $p = $p->getParent();
871        }
872
873        return $stack;
874    }
875
876    /**
877     * For debugging and other purposes.
878     *
879     * @return array<int, string>
880     */
881    public function getDFAStrings() : array
882    {
883        /** @var ParserATNSimulator $interp */
884        $interp = $this->getInterpreter();
885        $s = [];
886
887        /** @var DFA $dfa */
888        foreach ($interp->decisionToDFA as $dfa) {
889            $s[] = $dfa->toString($this->getVocabulary());
890        }
891
892        return $s;
893    }
894
895    /** For debugging and other purposes. */
896    public function dumpDFA() : void
897    {
898        /** @var ParserATNSimulator $interp */
899        $interp = $this->getInterpreter();
900        $seenOne = false;
901
902        /** @var DFA $dfa */
903        foreach ($interp->decisionToDFA as $dfa) {
904            if ($dfa->states->isEmpty()) {
905                continue;
906            }
907
908            if ($seenOne) {
909                echo \PHP_EOL;
910            }
911
912            echo \sprintf("Decision %d:\n%s", $dfa->decision, $dfa->toString($this->getVocabulary()));
913
914            $seenOne = true;
915        }
916    }
917
918    public function getSourceName() : string
919    {
920        return $this->tokenStream()->getSourceName();
921    }
922
923
924    /** During a parse is sometimes useful to listen in on the rule entry and exit
925     *  events as well as token matches. This is for quick and dirty debugging.
926     */
927    public function setTrace(bool $trace) : void
928    {
929        if ($this->tracer !== null) {
930            $this->removeParseListener($this->tracer);
931        }
932
933        if ($trace) {
934            $this->tracer = new ParserTraceListener($this);
935            $this->addParseListener($this->tracer);
936        }
937    }
938
939    /**
940     * Gets whether a {@see TraceListener} is registered as a parse listener
941     * for the parser.
942     *
943     * @see Parser::setTrace()
944     */
945    public function isTrace() : bool
946    {
947        return $this->tracer !== null;
948    }
949}
950