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