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