xref: /plugin/combo/vendor/antlr/antlr4-php-runtime/src/Lexer.php (revision 37748cd8654635afbeca80942126742f0f4cc346)
1*37748cd8SNickeau<?php
2*37748cd8SNickeau
3*37748cd8SNickeaudeclare(strict_types=1);
4*37748cd8SNickeau
5*37748cd8SNickeaunamespace Antlr\Antlr4\Runtime;
6*37748cd8SNickeau
7*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Atn\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