Parser   F
last analyzed

Complexity

Total Complexity 116

Size/Duplication

Total Lines 920
Duplicated Lines 0 %

Test Coverage

Coverage 52.82%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 240
c 2
b 0
f 0
dl 0
loc 920
ccs 159
cts 301
cp 0.5282
rs 2
wmc 116

55 Methods

Rating   Name   Duplication   Size   Complexity  
A tokenStream() 0 7 2
A getCurrentToken() 0 3 1
A __construct() 0 7 1
A setBuildParseTree() 0 3 1
A precpred() 0 3 1
A getNumberOfSyntaxErrors() 0 3 1
A setTokenFactory() 0 3 1
A getParseListeners() 0 3 1
A getDFAStrings() 0 12 2
A addParseListener() 0 4 2
A match() 0 23 6
A token() 0 9 2
A getContext() 0 3 1
A getExpectedTokensWithinCurrentRule() 0 6 1
A getErrorHandler() 0 3 1
A addContextToParseTree() 0 11 3
A setTrace() 0 9 3
A getCurrentRuleName() 0 3 1
A enterRule() 0 11 2
A enterOuterAlt() 0 17 4
A getBuildParseTree() 0 3 1
A setErrorHandler() 0 3 1
A setTokenStream() 0 5 1
B unrollRecursionContexts() 0 30 7
A context() 0 7 2
A dumpDFA() 0 19 4
A pushNewRecursionContext() 0 15 2
A isTrace() 0 3 1
A exitRule() 0 18 4
A triggerExitRuleEvent() 0 7 2
B consume() 0 29 9
A createErrorNode() 0 3 1
A reset() 0 17 3
A getTokenFactory() 0 3 1
A setContext() 0 3 1
A removeParseListeners() 0 3 1
A getExpectedTokens() 0 4 1
A getRuleInvocationStack() 0 20 3
A inContext() 0 4 1
A getSourceName() 0 3 1
A getATNWithBypassAlts() 0 13 2
A createTerminalNode() 0 3 1
A getRuleIndex() 0 3 1
A removeParseListener() 0 6 2
A enterRecursionRule() 0 8 1
A getInvokingContext() 0 12 3
A notifyErrorListeners() 0 14 2
B isExpectedToken() 0 32 8
A matchWildcard() 0 18 4
A isMatchedEOF() 0 3 1
A getPrecedence() 0 3 1
A triggerEnterRuleEvent() 0 5 2
A getTokenStream() 0 3 1
A setInputStream() 0 7 2
A getInputStream() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Parser often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Parser, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Antlr\Antlr4\Runtime;
6
7
use Antlr\Antlr4\Runtime\Atn\ATN;
8
use Antlr\Antlr4\Runtime\Atn\ATNDeserializationOptions;
9
use Antlr\Antlr4\Runtime\Atn\ATNDeserializer;
10
use Antlr\Antlr4\Runtime\Atn\ParserATNSimulator;
11
use Antlr\Antlr4\Runtime\Atn\States\ATNState;
12
use Antlr\Antlr4\Runtime\Atn\Transitions\RuleTransition;
13
use Antlr\Antlr4\Runtime\Dfa\DFA;
14
use Antlr\Antlr4\Runtime\Error\ANTLRErrorStrategy;
15
use Antlr\Antlr4\Runtime\Error\DefaultErrorStrategy;
16
use Antlr\Antlr4\Runtime\Error\Exceptions\InputMismatchException;
17
use Antlr\Antlr4\Runtime\Error\Exceptions\RecognitionException;
18
use Antlr\Antlr4\Runtime\Tree\ErrorNode;
19
use Antlr\Antlr4\Runtime\Tree\ErrorNodeImpl;
20
use Antlr\Antlr4\Runtime\Tree\ParseTreeListener;
21
use Antlr\Antlr4\Runtime\Tree\TerminalNode;
22
use 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
 */
28
abstract 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 7
    public function __construct(TokenStream $input)
120
    {
121 7
        parent::__construct();
122
123 7
        $this->errorHandler = new DefaultErrorStrategy();
124
125 7
        $this->setInputStream($input);
126 7
    }
127
128
    /** reset the parser's state */
129 7
    public function reset() : void
130
    {
131 7
        if ($this->input !== null) {
132
            $this->input->seek(0);
133
        }
134
135 7
        $this->errorHandler->reset($this);
136 7
        $this->ctx = null;
137 7
        $this->syntaxErrors = 0;
138 7
        $this->matchedEOF = false;
139 7
        $this->setTrace(false);
140 7
        $this->precedenceStack = [0];
141
142 7
        $interpreter = $this->getInterpreter();
143
144 7
        if ($interpreter !== null) {
145
            $interpreter->reset();
146
        }
147 7
    }
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 4
    public function match(int $ttype) : Token
172
    {
173 4
        $t = $this->getCurrentToken();
174
175 4
        if ($t !== null && $t->getType() === $ttype) {
176 4
            if ($ttype === Token::EOF) {
177 4
                $this->matchedEOF = true;
178
            }
179
180 4
            $this->errorHandler->reportMatch($this);
181
182 4
            $this->consume();
183
        } else {
184 1
            $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 4
        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 7
    public function setBuildParseTree(bool $buildParseTree) : void
251
    {
252 7
        $this->buildParseTree = $buildParseTree;
253 7
    }
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 3
    public function getParseListeners() : array
271
    {
272 3
        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 7
    protected function triggerEnterRuleEvent() : void
342
    {
343 7
        foreach ($this->parseListeners as $listener) {
344
            $listener->enterEveryRule($this->context());
345
            $this->context()->enterRule($listener);
346
        }
347 7
    }
348
349
    /**
350
     * Notify any parse listeners of an exit rule event.
351
     *
352
     * @see Parser::addParseListener()
353
     */
354 7
    protected function triggerExitRuleEvent() : void
355
    {
356 7
        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 7
    }
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 7
    public function getInputStream() : ?IntStream
424
    {
425 7
        return $this->getTokenStream();
426
    }
427
428 7
    final public function setInputStream(IntStream $input) : void
429
    {
430 7
        if (!$input instanceof TokenStream) {
431
            throw new \RuntimeException('The stream must be a token stream.');
432
        }
433
434 7
        $this->setTokenStream($input);
435 7
    }
436
437 7
    public function getTokenStream() : ?TokenStream
438
    {
439 7
        return $this->input;
440
    }
441
442 7
    private function tokenStream() : TokenStream
443
    {
444 7
        if ($this->input === null) {
445
            throw new \RuntimeException('The current token stream is null.');
446
        }
447
448 7
        return $this->input;
449
    }
450
451
    /** Set the token stream and reset the parser. */
452 7
    public function setTokenStream(TokenStream $input) : void
453
    {
454 7
        $this->input = null;
455 7
        $this->reset();
456 7
        $this->input = $input;
457 7
    }
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 7
    public function getCurrentToken() : ?Token
464
    {
465 7
        return $this->tokenStream()->LT(1);
466
    }
467
468 5
    private function token() : Token
469
    {
470 5
        $token = $this->getCurrentToken();
471
472 5
        if ($token === null) {
473
            throw new \RuntimeException('The current token is null.');
474
        }
475
476 5
        return $token;
477
    }
478
479 4
    final public function notifyErrorListeners(
480
        string $msg,
481
        ?Token $offendingToken = null,
482
        ?RecognitionException $e = null
483
    ) : void {
484 4
        if ($offendingToken === null) {
485
            $offendingToken = $this->token();
486
        }
487
488 4
        $this->syntaxErrors++;
489 4
        $line = $offendingToken->getLine();
490 4
        $charPositionInLine = $offendingToken->getCharPositionInLine();
491 4
        $listener = $this->getErrorListenerDispatch();
492 4
        $listener->syntaxError($this, $offendingToken, $line, $charPositionInLine, $msg, $e);
493 4
    }
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 5
    public function consume() : Token
517
    {
518 5
        $o = $this->token();
519
520 5
        if ($o->getType() !== self::EOF) {
521 5
            $this->tokenStream()->consume();
522
        }
523
524 5
        if ($this->buildParseTree || \count($this->parseListeners) > 0) {
525 5
            if ($this->errorHandler->inErrorRecoveryMode($this)) {
526 1
                $node = $this->context()->addErrorNode($this->createErrorNode($this->context(), $o));
527
528 1
                foreach ($this->parseListeners as $listener) {
529
                    if ($node instanceof ErrorNode) {
530
                        $listener->visitErrorNode($node);
531
                    }
532
                }
533
            } else {
534 4
                $node = $this->context()->addTerminalNode($this->createTerminalNode($this->context(), $o));
535
536 4
                foreach ($this->parseListeners as $listener) {
537
                    if ($node instanceof TerminalNode) {
538
                        $listener->visitTerminal($node);
539
                    }
540
                }
541
            }
542
        }
543
544 5
        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 4
    public function createTerminalNode(ParserRuleContext $parent, Token $t) : TerminalNode
0 ignored issues
show
Unused Code introduced by
The parameter $parent is not used and could be removed. ( Ignorable by Annotation )

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

552
    public function createTerminalNode(/** @scrutinizer ignore-unused */ ParserRuleContext $parent, Token $t) : TerminalNode

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

Loading history...
553
    {
554 4
        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 1
    public function createErrorNode(ParserRuleContext $parent, Token $t) : ErrorNode
0 ignored issues
show
Unused Code introduced by
The parameter $parent is not used and could be removed. ( Ignorable by Annotation )

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

562
    public function createErrorNode(/** @scrutinizer ignore-unused */ ParserRuleContext $parent, Token $t) : ErrorNode

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

Loading history...
563
    {
564 1
        return new ErrorNodeImpl($t);
565
    }
566
567 7
    protected function addContextToParseTree() : void
568
    {
569 7
        $parent = $this->context()->getParent();
570
571 7
        if ($parent === null) {
572 7
            return;
573
        }
574
575
        // add current context to parent if we have a parent
576 4
        if ($parent instanceof ParserRuleContext) {
577 4
            $parent->addChild($this->context());
578
        }
579 4
    }
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 7
    public function enterRule(ParserRuleContext $localctx, int $state, int $ruleIndex) : void
0 ignored issues
show
Unused Code introduced by
The parameter $ruleIndex is not used and could be removed. ( Ignorable by Annotation )

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

585
    public function enterRule(ParserRuleContext $localctx, int $state, /** @scrutinizer ignore-unused */ int $ruleIndex) : void

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

Loading history...
586
    {
587 7
        $this->setState($state);
588 7
        $this->ctx = $localctx;
589 7
        $this->context()->start = $this->tokenStream()->LT(1);
590
591 7
        if ($this->buildParseTree) {
592 7
            $this->addContextToParseTree();
593
        }
594
595 7
        $this->triggerEnterRuleEvent();
596 7
    }
597
598 7
    public function exitRule() : void
599
    {
600 7
        if ($this->matchedEOF) {
601
            // if we have matched EOF, it cannot consume past EOF so we use LT(1) here
602 4
            $this->context()->stop = $this->tokenStream()->LT(1); // LT(1) will be end of file
603
        } else {
604 7
            $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 7
        $this->triggerExitRuleEvent();
609
610 7
        $this->setState($this->context()->invokingState);
611
612 7
        $parent = $this->context()->getParent();
613
614 7
        if ($parent === null || $parent instanceof ParserRuleContext) {
615 7
            $this->ctx = $parent;
616
        }
617 7
    }
618
619 4
    public function enterOuterAlt(ParserRuleContext $localctx, int $altNum) : void
620
    {
621 4
        $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 4
        if ($this->buildParseTree && $this->ctx !== $localctx) {
626
            /** @var ParserRuleContext $parent */
627 4
            $parent = $this->context()->getParent();
628
629 4
            if ($parent !== null) {
630 4
                $parent->removeLastChild();
631 4
                $parent->addChild($localctx);
632
            }
633
        }
634
635 4
        $this->ctx = $localctx;
636 4
    }
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 4
    public function getPrecedence() : int
645
    {
646 4
        return $this->precedenceStack[\count($this->precedenceStack) - 1] ?? -1;
647
    }
648
649 4
    public function enterRecursionRule(ParserRuleContext $localctx, int $state, int $ruleIndex, int $precedence) : void
0 ignored issues
show
Unused Code introduced by
The parameter $ruleIndex is not used and could be removed. ( Ignorable by Annotation )

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

649
    public function enterRecursionRule(ParserRuleContext $localctx, int $state, /** @scrutinizer ignore-unused */ int $ruleIndex, int $precedence) : void

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

Loading history...
650
    {
651 4
        $this->setState($state);
652 4
        $this->precedenceStack[] = $precedence;
653 4
        $this->ctx = $localctx;
654 4
        $this->context()->start = $this->tokenStream()->LT(1);
655
656 4
        $this->triggerEnterRuleEvent(); // simulates rule entry for left-recursive rules
657 4
    }
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 3
    public function pushNewRecursionContext(ParserRuleContext $localctx, int $state, int $ruleIndex) : void
0 ignored issues
show
Unused Code introduced by
The parameter $ruleIndex is not used and could be removed. ( Ignorable by Annotation )

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

664
    public function pushNewRecursionContext(ParserRuleContext $localctx, int $state, /** @scrutinizer ignore-unused */ int $ruleIndex) : void

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

Loading history...
665
    {
666 3
        $previous = $this->context();
667 3
        $previous->setParent($localctx);
668 3
        $previous->invokingState = $state;
669 3
        $previous->stop = $this->tokenStream()->LT(-1);
670
671 3
        $this->ctx = $localctx;
672 3
        $this->context()->start = $previous->start;
673
674 3
        if ($this->buildParseTree) {
675 3
            $this->context()->addChild($previous);
676
        }
677
678 3
        $this->triggerEnterRuleEvent(); // simulates rule entry for left-recursive rules
679 3
    }
680
681 4
    public function unrollRecursionContexts(?ParserRuleContext $parentctx) : void
682
    {
683 4
        \array_pop($this->precedenceStack);
684
685 4
        $this->context()->stop = $this->tokenStream()->LT(-1);
686 4
        $retctx = $this->context(); // save current ctx (return value)
687
688
        // unroll so _ctx is as it was before call to recursive method
689
690 4
        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 4
            $this->ctx = $parentctx;
703
        }
704
705
        // hook into tree
706 4
        $retctx->setParent($parentctx);
707
708 4
        if ($this->buildParseTree && $parentctx !== null) {
709
            // add return ctx into invoking rule's tree
710 4
            $parentctx->addChild($retctx);
711
        }
712 4
    }
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 7
    public function getContext() : ?ParserRuleContext
729
    {
730 7
        return $this->ctx;
731
    }
732
733 7
    private function context() : ParserRuleContext
734
    {
735 7
        if ($this->ctx === null) {
736
            throw new \RuntimeException('The current context is null.');
737
        }
738
739 7
        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 3
    public function precpred(RuleContext $localctx, int $precedence) : bool
753
    {
754 3
        return $precedence >= $this->getPrecedence();
755
    }
756
757
    public function inContext(string $context) : bool
0 ignored issues
show
Unused Code introduced by
The parameter $context is not used and could be removed. ( Ignorable by Annotation )

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

757
    public function inContext(/** @scrutinizer ignore-unused */ string $context) : bool

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

Loading history...
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 4
    public function getExpectedTokens() : IntervalSet
825
    {
826 4
        return $this->getATN()
827 4
            ->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 7
    public function setTrace(bool $trace) : void
928
    {
929 7
        if ($this->tracer !== null) {
930
            $this->removeParseListener($this->tracer);
931
        }
932
933 7
        if ($trace) {
934
            $this->tracer = new ParserTraceListener($this);
935
            $this->addParseListener($this->tracer);
936
        }
937 7
    }
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