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\LexerATNSimulator; 8*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Error\Exceptions\LexerNoViableAltException; 9*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Error\Exceptions\RecognitionException; 10*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Utils\Pair; 11*37748cd8SNickeau 12*37748cd8SNickeau/** 13*37748cd8SNickeau * A lexer is recognizer that draws input symbols from a character stream. 14*37748cd8SNickeau * lexer grammars result in a subclass of this object. A Lexer object 15*37748cd8SNickeau * uses simplified match() and error recovery mechanisms in the interest 16*37748cd8SNickeau * of speed. 17*37748cd8SNickeau */ 18*37748cd8SNickeauabstract class Lexer extends Recognizer implements TokenSource 19*37748cd8SNickeau{ 20*37748cd8SNickeau public const DEFAULT_MODE = 0; 21*37748cd8SNickeau public const MORE = -2; 22*37748cd8SNickeau public const SKIP = -3; 23*37748cd8SNickeau 24*37748cd8SNickeau public const DEFAULT_TOKEN_CHANNEL = Token::DEFAULT_CHANNEL; 25*37748cd8SNickeau public const HIDDEN = Token::HIDDEN_CHANNEL; 26*37748cd8SNickeau public const MIN_CHAR_VALUE = 0x0000; 27*37748cd8SNickeau public const MAX_CHAR_VALUE = 0x10FFFF; 28*37748cd8SNickeau 29*37748cd8SNickeau /** @var CharStream|null */ 30*37748cd8SNickeau public $input; 31*37748cd8SNickeau 32*37748cd8SNickeau /** @var Pair Pair<TokenSource, CharStream> */ 33*37748cd8SNickeau protected $tokenFactorySourcePair; 34*37748cd8SNickeau 35*37748cd8SNickeau /** @var TokenFactory */ 36*37748cd8SNickeau protected $factory; 37*37748cd8SNickeau 38*37748cd8SNickeau /** 39*37748cd8SNickeau * The goal of all lexer rules/methods is to create a token object. 40*37748cd8SNickeau * This is an instance variable as multiple rules may collaborate to 41*37748cd8SNickeau * create a single token. `nextToken` will return this object after 42*37748cd8SNickeau * matching lexer rule(s). 43*37748cd8SNickeau * 44*37748cd8SNickeau * If you subclass to allow multiple token emissions, then set this 45*37748cd8SNickeau * to the last token to be matched or something nonnull so that 46*37748cd8SNickeau * the auto token emit mechanism will not emit another token. 47*37748cd8SNickeau * 48*37748cd8SNickeau * @var Token|null 49*37748cd8SNickeau */ 50*37748cd8SNickeau public $token; 51*37748cd8SNickeau 52*37748cd8SNickeau /** 53*37748cd8SNickeau * What character index in the stream did the current token start at? 54*37748cd8SNickeau * Needed, for example, to get the text for current token. Set at 55*37748cd8SNickeau * the start of nextToken. 56*37748cd8SNickeau * 57*37748cd8SNickeau * @var int 58*37748cd8SNickeau */ 59*37748cd8SNickeau public $tokenStartCharIndex = -1; 60*37748cd8SNickeau 61*37748cd8SNickeau /** 62*37748cd8SNickeau * The line on which the first character of the token resides. 63*37748cd8SNickeau * 64*37748cd8SNickeau * @var int 65*37748cd8SNickeau */ 66*37748cd8SNickeau public $tokenStartLine = -1; 67*37748cd8SNickeau 68*37748cd8SNickeau /** 69*37748cd8SNickeau * The character position of first character within the line 70*37748cd8SNickeau * 71*37748cd8SNickeau * @var int 72*37748cd8SNickeau */ 73*37748cd8SNickeau public $tokenStartCharPositionInLine = -1; 74*37748cd8SNickeau 75*37748cd8SNickeau /** 76*37748cd8SNickeau * Once we see EOF on char stream, next token will be EOF. 77*37748cd8SNickeau * If you have DONE : EOF ; then you see DONE EOF. 78*37748cd8SNickeau * 79*37748cd8SNickeau * @var bool 80*37748cd8SNickeau */ 81*37748cd8SNickeau public $hitEOF = false; 82*37748cd8SNickeau 83*37748cd8SNickeau /** 84*37748cd8SNickeau * The channel number for the current token. 85*37748cd8SNickeau * 86*37748cd8SNickeau * @var int 87*37748cd8SNickeau */ 88*37748cd8SNickeau public $channel = Token::DEFAULT_CHANNEL; 89*37748cd8SNickeau 90*37748cd8SNickeau /** 91*37748cd8SNickeau * The token type for the current token. 92*37748cd8SNickeau * 93*37748cd8SNickeau * @var int 94*37748cd8SNickeau */ 95*37748cd8SNickeau public $type = Token::INVALID_TYPE; 96*37748cd8SNickeau 97*37748cd8SNickeau /** @var array<int> */ 98*37748cd8SNickeau public $modeStack = []; 99*37748cd8SNickeau 100*37748cd8SNickeau /** @var int */ 101*37748cd8SNickeau public $mode = self::DEFAULT_MODE; 102*37748cd8SNickeau 103*37748cd8SNickeau /** 104*37748cd8SNickeau * You can set the text for the current token to override what is in the 105*37748cd8SNickeau * input char buffer. Use {@see Lexer::setText()} or can set this instance var. 106*37748cd8SNickeau * 107*37748cd8SNickeau * @var string|null 108*37748cd8SNickeau */ 109*37748cd8SNickeau public $text; 110*37748cd8SNickeau 111*37748cd8SNickeau /** @var LexerATNSimulator|null */ 112*37748cd8SNickeau protected $interp; 113*37748cd8SNickeau 114*37748cd8SNickeau public function __construct(?CharStream $input = null) 115*37748cd8SNickeau { 116*37748cd8SNickeau parent::__construct(); 117*37748cd8SNickeau 118*37748cd8SNickeau $this->input = $input; 119*37748cd8SNickeau $this->factory = CommonTokenFactory::default(); 120*37748cd8SNickeau $this->tokenFactorySourcePair = new Pair($this, $input); 121*37748cd8SNickeau 122*37748cd8SNickeau // @todo remove this property 123*37748cd8SNickeau $this->interp = null;// child classes must populate this 124*37748cd8SNickeau } 125*37748cd8SNickeau 126*37748cd8SNickeau public function reset() : void 127*37748cd8SNickeau { 128*37748cd8SNickeau // wack Lexer state variables 129*37748cd8SNickeau if ($this->input !== null) { 130*37748cd8SNickeau $this->input->seek(0);// rewind the input 131*37748cd8SNickeau } 132*37748cd8SNickeau 133*37748cd8SNickeau $this->token = null; 134*37748cd8SNickeau $this->type = Token::INVALID_TYPE; 135*37748cd8SNickeau $this->channel = Token::DEFAULT_CHANNEL; 136*37748cd8SNickeau $this->tokenStartCharIndex = -1; 137*37748cd8SNickeau $this->tokenStartCharPositionInLine = -1; 138*37748cd8SNickeau $this->tokenStartLine = -1; 139*37748cd8SNickeau $this->text = null; 140*37748cd8SNickeau 141*37748cd8SNickeau $this->hitEOF = false; 142*37748cd8SNickeau $this->mode = self::DEFAULT_MODE; 143*37748cd8SNickeau $this->modeStack = []; 144*37748cd8SNickeau 145*37748cd8SNickeau if ($this->interp !== null) { 146*37748cd8SNickeau $this->interp->reset(); 147*37748cd8SNickeau } 148*37748cd8SNickeau } 149*37748cd8SNickeau 150*37748cd8SNickeau /** 151*37748cd8SNickeau * Return a token from this source; i.e., match a token on the char stream. 152*37748cd8SNickeau */ 153*37748cd8SNickeau public function nextToken() : ?Token 154*37748cd8SNickeau { 155*37748cd8SNickeau if ($this->input === null) { 156*37748cd8SNickeau throw new \RuntimeException('NextToken requires a non-null input stream.'); 157*37748cd8SNickeau } 158*37748cd8SNickeau 159*37748cd8SNickeau // Mark start location in char stream so unbuffered streams are 160*37748cd8SNickeau // guaranteed at least have text of current token 161*37748cd8SNickeau $tokenStartMarker = $this->input->mark(); 162*37748cd8SNickeau 163*37748cd8SNickeau try { 164*37748cd8SNickeau while (true) { 165*37748cd8SNickeau if ($this->hitEOF) { 166*37748cd8SNickeau $this->emitEOF(); 167*37748cd8SNickeau 168*37748cd8SNickeau return $this->token; 169*37748cd8SNickeau } 170*37748cd8SNickeau 171*37748cd8SNickeau if ($this->interp === null || !$this->interp instanceof LexerATNSimulator) { 172*37748cd8SNickeau throw new \RuntimeException('Unexpected interpreter type.'); 173*37748cd8SNickeau } 174*37748cd8SNickeau 175*37748cd8SNickeau $this->token = null; 176*37748cd8SNickeau $this->channel = Token::DEFAULT_CHANNEL; 177*37748cd8SNickeau $this->tokenStartCharIndex = $this->input->getIndex(); 178*37748cd8SNickeau $this->tokenStartCharPositionInLine = $this->interp->getCharPositionInLine(); 179*37748cd8SNickeau $this->tokenStartLine = $this->interp->getLine(); 180*37748cd8SNickeau $this->text = null; 181*37748cd8SNickeau $continueOuter = false; 182*37748cd8SNickeau 183*37748cd8SNickeau while (true) { 184*37748cd8SNickeau $this->type = Token::INVALID_TYPE; 185*37748cd8SNickeau $ttype = self::SKIP; 186*37748cd8SNickeau try { 187*37748cd8SNickeau $ttype = $this->interp->match($this->input, $this->mode); 188*37748cd8SNickeau } catch (LexerNoViableAltException $e) { 189*37748cd8SNickeau $this->notifyListeners($e); // report error 190*37748cd8SNickeau $this->recover($e); 191*37748cd8SNickeau } 192*37748cd8SNickeau 193*37748cd8SNickeau if ($this->input->LA(1) === Token::EOF) { 194*37748cd8SNickeau $this->hitEOF = true; 195*37748cd8SNickeau } 196*37748cd8SNickeau 197*37748cd8SNickeau if ($this->type === Token::INVALID_TYPE) { 198*37748cd8SNickeau $this->type = $ttype; 199*37748cd8SNickeau } 200*37748cd8SNickeau 201*37748cd8SNickeau if ($this->type === self::SKIP) { 202*37748cd8SNickeau $continueOuter = true; 203*37748cd8SNickeau 204*37748cd8SNickeau break; 205*37748cd8SNickeau } 206*37748cd8SNickeau 207*37748cd8SNickeau if ($this->type !== self::MORE) { 208*37748cd8SNickeau break; 209*37748cd8SNickeau } 210*37748cd8SNickeau } 211*37748cd8SNickeau 212*37748cd8SNickeau if ($continueOuter) { 213*37748cd8SNickeau continue; 214*37748cd8SNickeau } 215*37748cd8SNickeau 216*37748cd8SNickeau if ($this->token === null) { 217*37748cd8SNickeau $this->emit(); 218*37748cd8SNickeau } 219*37748cd8SNickeau 220*37748cd8SNickeau return $this->token; 221*37748cd8SNickeau } 222*37748cd8SNickeau } finally { 223*37748cd8SNickeau // make sure we release marker after match or 224*37748cd8SNickeau // unbuffered char stream will keep buffering 225*37748cd8SNickeau $this->input->release($tokenStartMarker); 226*37748cd8SNickeau } 227*37748cd8SNickeau } 228*37748cd8SNickeau 229*37748cd8SNickeau /** 230*37748cd8SNickeau * Instruct the lexer to skip creating a token for current lexer rule 231*37748cd8SNickeau * and look for another token. `nextToken` knows to keep looking when 232*37748cd8SNickeau * a lexer rule finishes with token set to SKIP_TOKEN. Recall that 233*37748cd8SNickeau * if `token === null` at end of any token rule, it creates one for you 234*37748cd8SNickeau * and emits it. 235*37748cd8SNickeau */ 236*37748cd8SNickeau public function skip() : void 237*37748cd8SNickeau { 238*37748cd8SNickeau $this->type = self::SKIP; 239*37748cd8SNickeau } 240*37748cd8SNickeau 241*37748cd8SNickeau public function more() : void 242*37748cd8SNickeau { 243*37748cd8SNickeau $this->type = self::MORE; 244*37748cd8SNickeau } 245*37748cd8SNickeau 246*37748cd8SNickeau public function mode(int $m) : void 247*37748cd8SNickeau { 248*37748cd8SNickeau $this->mode = $m; 249*37748cd8SNickeau } 250*37748cd8SNickeau 251*37748cd8SNickeau public function pushMode(int $m) : void 252*37748cd8SNickeau { 253*37748cd8SNickeau $this->modeStack[] = $this->mode; 254*37748cd8SNickeau 255*37748cd8SNickeau $this->mode($m); 256*37748cd8SNickeau } 257*37748cd8SNickeau 258*37748cd8SNickeau public function popMode() : int 259*37748cd8SNickeau { 260*37748cd8SNickeau if (\count($this->modeStack) === 0) { 261*37748cd8SNickeau throw new \RuntimeException('Empty Stack'); 262*37748cd8SNickeau } 263*37748cd8SNickeau 264*37748cd8SNickeau $this->mode(\array_pop($this->modeStack)); 265*37748cd8SNickeau 266*37748cd8SNickeau return $this->mode; 267*37748cd8SNickeau } 268*37748cd8SNickeau 269*37748cd8SNickeau public function getSourceName() : string 270*37748cd8SNickeau { 271*37748cd8SNickeau return $this->input === null ? '' : $this->input->getSourceName(); 272*37748cd8SNickeau } 273*37748cd8SNickeau 274*37748cd8SNickeau public function getInputStream() : ?IntStream 275*37748cd8SNickeau { 276*37748cd8SNickeau return $this->input; 277*37748cd8SNickeau } 278*37748cd8SNickeau 279*37748cd8SNickeau public function getTokenFactory() : TokenFactory 280*37748cd8SNickeau { 281*37748cd8SNickeau return $this->factory; 282*37748cd8SNickeau } 283*37748cd8SNickeau 284*37748cd8SNickeau public function setTokenFactory(TokenFactory $factory) : void 285*37748cd8SNickeau { 286*37748cd8SNickeau $this->factory = $factory; 287*37748cd8SNickeau } 288*37748cd8SNickeau 289*37748cd8SNickeau public function setInputStream(IntStream $input) : void 290*37748cd8SNickeau { 291*37748cd8SNickeau $this->input = null; 292*37748cd8SNickeau $this->tokenFactorySourcePair = new Pair($this, $this->input); 293*37748cd8SNickeau 294*37748cd8SNickeau $this->reset(); 295*37748cd8SNickeau 296*37748cd8SNickeau if (!$input instanceof CharStream) { 297*37748cd8SNickeau throw new \RuntimeException('Input must be CharStream.'); 298*37748cd8SNickeau } 299*37748cd8SNickeau 300*37748cd8SNickeau $this->input = $input; 301*37748cd8SNickeau $this->tokenFactorySourcePair = new Pair($this, $this->input); 302*37748cd8SNickeau } 303*37748cd8SNickeau 304*37748cd8SNickeau /** 305*37748cd8SNickeau * By default does not support multiple emits per nextToken invocation 306*37748cd8SNickeau * for efficiency reasons. Subclass and override this method, nextToken, 307*37748cd8SNickeau * and getToken (to push tokens into a list and pull from that list 308*37748cd8SNickeau * rather than a single variable as this implementation does). 309*37748cd8SNickeau */ 310*37748cd8SNickeau public function emitToken(Token $token) : void 311*37748cd8SNickeau { 312*37748cd8SNickeau $this->token = $token; 313*37748cd8SNickeau } 314*37748cd8SNickeau 315*37748cd8SNickeau /** 316*37748cd8SNickeau * The standard method called to automatically emit a token at the 317*37748cd8SNickeau * outermost lexical rule. The token object should point into the 318*37748cd8SNickeau * char buffer start..stop. If there is a text override in 'text', 319*37748cd8SNickeau * use that to set the token's text. Override this method to emit 320*37748cd8SNickeau * custom Token objects or provide a new factory. 321*37748cd8SNickeau */ 322*37748cd8SNickeau public function emit() : Token 323*37748cd8SNickeau { 324*37748cd8SNickeau $token = $this->factory->createEx( 325*37748cd8SNickeau $this->tokenFactorySourcePair, 326*37748cd8SNickeau $this->type, 327*37748cd8SNickeau $this->text, 328*37748cd8SNickeau $this->channel, 329*37748cd8SNickeau $this->tokenStartCharIndex, 330*37748cd8SNickeau $this->getCharIndex() - 1, 331*37748cd8SNickeau $this->tokenStartLine, 332*37748cd8SNickeau $this->tokenStartCharPositionInLine 333*37748cd8SNickeau ); 334*37748cd8SNickeau 335*37748cd8SNickeau $this->emitToken($token); 336*37748cd8SNickeau 337*37748cd8SNickeau return $token; 338*37748cd8SNickeau } 339*37748cd8SNickeau 340*37748cd8SNickeau public function emitEOF() : Token 341*37748cd8SNickeau { 342*37748cd8SNickeau if ($this->input === null) { 343*37748cd8SNickeau throw new \RuntimeException('Cannot emit EOF for null stream.'); 344*37748cd8SNickeau } 345*37748cd8SNickeau 346*37748cd8SNickeau $cpos = $this->getCharPositionInLine(); 347*37748cd8SNickeau $lpos = $this->getLine(); 348*37748cd8SNickeau $eof = $this->factory->createEx( 349*37748cd8SNickeau $this->tokenFactorySourcePair, 350*37748cd8SNickeau Token::EOF, 351*37748cd8SNickeau null, 352*37748cd8SNickeau Token::DEFAULT_CHANNEL, 353*37748cd8SNickeau $this->input->getIndex(), 354*37748cd8SNickeau $this->input->getIndex() - 1, 355*37748cd8SNickeau $lpos, 356*37748cd8SNickeau $cpos 357*37748cd8SNickeau ); 358*37748cd8SNickeau 359*37748cd8SNickeau $this->emitToken($eof); 360*37748cd8SNickeau 361*37748cd8SNickeau return $eof; 362*37748cd8SNickeau } 363*37748cd8SNickeau 364*37748cd8SNickeau public function getLine() : int 365*37748cd8SNickeau { 366*37748cd8SNickeau if ($this->interp === null || !$this->interp instanceof LexerATNSimulator) { 367*37748cd8SNickeau throw new \RuntimeException('Unexpected interpreter type.'); 368*37748cd8SNickeau } 369*37748cd8SNickeau 370*37748cd8SNickeau return $this->interp->getLine(); 371*37748cd8SNickeau } 372*37748cd8SNickeau 373*37748cd8SNickeau public function setLine(int $line) : void 374*37748cd8SNickeau { 375*37748cd8SNickeau if ($this->interp === null || !$this->interp instanceof LexerATNSimulator) { 376*37748cd8SNickeau throw new \RuntimeException('Unexpected interpreter type.'); 377*37748cd8SNickeau } 378*37748cd8SNickeau 379*37748cd8SNickeau $this->interp->setLine($line); 380*37748cd8SNickeau } 381*37748cd8SNickeau 382*37748cd8SNickeau public function getCharPositionInLine() : int 383*37748cd8SNickeau { 384*37748cd8SNickeau if ($this->interp === null || !$this->interp instanceof LexerATNSimulator) { 385*37748cd8SNickeau throw new \RuntimeException('Unexpected interpreter type.'); 386*37748cd8SNickeau } 387*37748cd8SNickeau 388*37748cd8SNickeau return $this->interp->getCharPositionInLine(); 389*37748cd8SNickeau } 390*37748cd8SNickeau 391*37748cd8SNickeau public function setCharPositionInLine(int $charPositionInLine) : void 392*37748cd8SNickeau { 393*37748cd8SNickeau if ($this->interp === null || !$this->interp instanceof LexerATNSimulator) { 394*37748cd8SNickeau throw new \RuntimeException('Unexpected interpreter type.'); 395*37748cd8SNickeau } 396*37748cd8SNickeau 397*37748cd8SNickeau $this->interp->setCharPositionInLine($charPositionInLine); 398*37748cd8SNickeau } 399*37748cd8SNickeau 400*37748cd8SNickeau /** 401*37748cd8SNickeau * What is the index of the current character of lookahead? 402*37748cd8SNickeau */ 403*37748cd8SNickeau public function getCharIndex() : int 404*37748cd8SNickeau { 405*37748cd8SNickeau if ($this->input === null) { 406*37748cd8SNickeau throw new \RuntimeException('Cannot know char index for null stream.'); 407*37748cd8SNickeau } 408*37748cd8SNickeau 409*37748cd8SNickeau return $this->input->getIndex(); 410*37748cd8SNickeau } 411*37748cd8SNickeau 412*37748cd8SNickeau /** 413*37748cd8SNickeau * Return the text matched so far for the current token or any text override. 414*37748cd8SNickeau */ 415*37748cd8SNickeau public function getText() : string 416*37748cd8SNickeau { 417*37748cd8SNickeau if ($this->text !== null) { 418*37748cd8SNickeau return $this->text; 419*37748cd8SNickeau } 420*37748cd8SNickeau 421*37748cd8SNickeau if ($this->interp === null || !$this->interp instanceof LexerATNSimulator) { 422*37748cd8SNickeau throw new \RuntimeException('Unexpected interpreter type.'); 423*37748cd8SNickeau } 424*37748cd8SNickeau 425*37748cd8SNickeau return $this->input === null ? '' : $this->interp->getText($this->input); 426*37748cd8SNickeau } 427*37748cd8SNickeau 428*37748cd8SNickeau /** 429*37748cd8SNickeau * Set the complete text of this token; it wipes any previous changes to the text. 430*37748cd8SNickeau */ 431*37748cd8SNickeau public function setText(string $text) : void 432*37748cd8SNickeau { 433*37748cd8SNickeau $this->text = $text; 434*37748cd8SNickeau } 435*37748cd8SNickeau 436*37748cd8SNickeau public function getToken() : ?Token 437*37748cd8SNickeau { 438*37748cd8SNickeau return $this->token; 439*37748cd8SNickeau } 440*37748cd8SNickeau 441*37748cd8SNickeau /** 442*37748cd8SNickeau * Override if emitting multiple tokens. 443*37748cd8SNickeau */ 444*37748cd8SNickeau public function setToken(Token $token) : void 445*37748cd8SNickeau { 446*37748cd8SNickeau $this->token = $token; 447*37748cd8SNickeau } 448*37748cd8SNickeau 449*37748cd8SNickeau public function getType() : int 450*37748cd8SNickeau { 451*37748cd8SNickeau return $this->type; 452*37748cd8SNickeau } 453*37748cd8SNickeau 454*37748cd8SNickeau public function setType(int $type) : void 455*37748cd8SNickeau { 456*37748cd8SNickeau $this->type = $type; 457*37748cd8SNickeau } 458*37748cd8SNickeau 459*37748cd8SNickeau public function getChannel() : int 460*37748cd8SNickeau { 461*37748cd8SNickeau return $this->channel; 462*37748cd8SNickeau } 463*37748cd8SNickeau 464*37748cd8SNickeau public function setChannel(int $channel) : void 465*37748cd8SNickeau { 466*37748cd8SNickeau $this->channel = $channel; 467*37748cd8SNickeau } 468*37748cd8SNickeau 469*37748cd8SNickeau /** 470*37748cd8SNickeau * @return array<string>|null 471*37748cd8SNickeau */ 472*37748cd8SNickeau public function getChannelNames() : ?array 473*37748cd8SNickeau { 474*37748cd8SNickeau return null; 475*37748cd8SNickeau } 476*37748cd8SNickeau 477*37748cd8SNickeau /** 478*37748cd8SNickeau * @return array<string>|null 479*37748cd8SNickeau */ 480*37748cd8SNickeau public function getModeNames() : ?array 481*37748cd8SNickeau { 482*37748cd8SNickeau return null; 483*37748cd8SNickeau } 484*37748cd8SNickeau 485*37748cd8SNickeau /** 486*37748cd8SNickeau * Return a list of all Token objects in input char stream. 487*37748cd8SNickeau * Forces load of all tokens. Does not include EOF token. 488*37748cd8SNickeau * 489*37748cd8SNickeau * @return array<Token> 490*37748cd8SNickeau */ 491*37748cd8SNickeau public function getAllTokens() : array 492*37748cd8SNickeau { 493*37748cd8SNickeau $tokens = []; 494*37748cd8SNickeau $token = $this->nextToken(); 495*37748cd8SNickeau 496*37748cd8SNickeau while ($token && $token->getType() !== Token::EOF) { 497*37748cd8SNickeau $tokens[] = $token; 498*37748cd8SNickeau $token = $this->nextToken(); 499*37748cd8SNickeau } 500*37748cd8SNickeau 501*37748cd8SNickeau return $tokens; 502*37748cd8SNickeau } 503*37748cd8SNickeau 504*37748cd8SNickeau /** 505*37748cd8SNickeau * Lexers can normally match any char in it's vocabulary after matching 506*37748cd8SNickeau * a token, so do the easy thing and just kill a character and hope 507*37748cd8SNickeau * it all works out. You can instead use the rule invocation stack 508*37748cd8SNickeau * to do sophisticated error recovery if you are in a fragment rule. 509*37748cd8SNickeau */ 510*37748cd8SNickeau public function recover(RecognitionException $re) : void 511*37748cd8SNickeau { 512*37748cd8SNickeau if ($this->input !== null && $this->input->LA(1) !== Token::EOF) { 513*37748cd8SNickeau if ($re instanceof LexerNoViableAltException && $this->interp !== null) { 514*37748cd8SNickeau // skip a char and try again 515*37748cd8SNickeau $this->interp->consume($this->input); 516*37748cd8SNickeau } else { 517*37748cd8SNickeau // TODO: Do we lose character or line position information? 518*37748cd8SNickeau $this->input->consume(); 519*37748cd8SNickeau } 520*37748cd8SNickeau } 521*37748cd8SNickeau } 522*37748cd8SNickeau 523*37748cd8SNickeau public function notifyListeners(LexerNoViableAltException $e) : void 524*37748cd8SNickeau { 525*37748cd8SNickeau $start = $this->tokenStartCharIndex; 526*37748cd8SNickeau 527*37748cd8SNickeau if ($this->input === null) { 528*37748cd8SNickeau $text = ''; 529*37748cd8SNickeau } else { 530*37748cd8SNickeau $stop = $this->input->getIndex(); 531*37748cd8SNickeau $text = $this->input->getText($start, $stop); 532*37748cd8SNickeau } 533*37748cd8SNickeau 534*37748cd8SNickeau $listener = $this->getErrorListenerDispatch(); 535*37748cd8SNickeau 536*37748cd8SNickeau $listener->syntaxError( 537*37748cd8SNickeau $this, 538*37748cd8SNickeau null, 539*37748cd8SNickeau $this->tokenStartLine, 540*37748cd8SNickeau $this->tokenStartCharPositionInLine, 541*37748cd8SNickeau \sprintf('token recognition error at: \'%s\'', $text), 542*37748cd8SNickeau $e 543*37748cd8SNickeau ); 544*37748cd8SNickeau } 545*37748cd8SNickeau} 546