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