xref: /plugin/combo/vendor/antlr/antlr4-php-runtime/src/Atn/LexerATNSimulator.php (revision 37748cd8654635afbeca80942126742f0f4cc346)
1*37748cd8SNickeau<?php
2*37748cd8SNickeau
3*37748cd8SNickeaudeclare(strict_types=1);
4*37748cd8SNickeau
5*37748cd8SNickeaunamespace Antlr\Antlr4\Runtime\Atn;
6*37748cd8SNickeau
7*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Atn\States\ATNState;
8*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Atn\States\RuleStopState;
9*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Atn\Transitions\ActionTransition;
10*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Atn\Transitions\PredicateTransition;
11*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Atn\Transitions\RuleTransition;
12*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Atn\Transitions\Transition;
13*37748cd8SNickeauuse Antlr\Antlr4\Runtime\CharStream;
14*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Dfa\DFA;
15*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Dfa\DFAState;
16*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Error\Exceptions\LexerNoViableAltException;
17*37748cd8SNickeauuse Antlr\Antlr4\Runtime\IntStream;
18*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Lexer;
19*37748cd8SNickeauuse Antlr\Antlr4\Runtime\PredictionContexts\PredictionContext;
20*37748cd8SNickeauuse Antlr\Antlr4\Runtime\PredictionContexts\PredictionContextCache;
21*37748cd8SNickeauuse Antlr\Antlr4\Runtime\PredictionContexts\SingletonPredictionContext;
22*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Token;
23*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Utils\StringUtils;
24*37748cd8SNickeau
25*37748cd8SNickeauclass LexerATNSimulator extends ATNSimulator
26*37748cd8SNickeau{
27*37748cd8SNickeau    public const MIN_DFA_EDGE = 0;
28*37748cd8SNickeau    public const MAX_DFA_EDGE = 127; // forces unicode to stay in ATN
29*37748cd8SNickeau    private const NEW_LINE_CODE = 10;
30*37748cd8SNickeau
31*37748cd8SNickeau    /** @var array<string> */
32*37748cd8SNickeau    public $log = [];
33*37748cd8SNickeau
34*37748cd8SNickeau    /** @var bool */
35*37748cd8SNickeau    public $debug = false;
36*37748cd8SNickeau
37*37748cd8SNickeau    /** @var Lexer|null */
38*37748cd8SNickeau    protected $recog;
39*37748cd8SNickeau
40*37748cd8SNickeau    /**
41*37748cd8SNickeau     * The current token's starting index into the character stream.
42*37748cd8SNickeau     * Shared across DFA to ATN simulation in case the ATN fails and the
43*37748cd8SNickeau     * DFA did not have a previous accept state. In this case, we use the
44*37748cd8SNickeau     * ATN-generated exception object.
45*37748cd8SNickeau     *
46*37748cd8SNickeau     * @var int
47*37748cd8SNickeau     */
48*37748cd8SNickeau    protected $startIndex = -1;
49*37748cd8SNickeau
50*37748cd8SNickeau    /**
51*37748cd8SNickeau     * The line number 1..n within the input.
52*37748cd8SNickeau     *
53*37748cd8SNickeau     * @var int
54*37748cd8SNickeau     */
55*37748cd8SNickeau    protected $line = 1;
56*37748cd8SNickeau
57*37748cd8SNickeau    /**
58*37748cd8SNickeau     * The index of the character relative to the beginning of the line 0..n-1.
59*37748cd8SNickeau     *
60*37748cd8SNickeau     * @var int
61*37748cd8SNickeau     */
62*37748cd8SNickeau    protected $charPositionInLine = 0;
63*37748cd8SNickeau
64*37748cd8SNickeau    /** @var array<DFA> */
65*37748cd8SNickeau    public $decisionToDFA = [];
66*37748cd8SNickeau
67*37748cd8SNickeau    /** @var int */
68*37748cd8SNickeau    protected $mode = Lexer::DEFAULT_MODE;
69*37748cd8SNickeau
70*37748cd8SNickeau    /**
71*37748cd8SNickeau     * Used during DFA/ATN exec to record the most recent accept configuration info.
72*37748cd8SNickeau     *
73*37748cd8SNickeau     * @var SimState
74*37748cd8SNickeau     */
75*37748cd8SNickeau    protected $prevAccept;
76*37748cd8SNickeau
77*37748cd8SNickeau    /**
78*37748cd8SNickeau     * @param array<DFA> $decisionToDFA
79*37748cd8SNickeau     */
80*37748cd8SNickeau    public function __construct(
81*37748cd8SNickeau        ?Lexer $recog,
82*37748cd8SNickeau        ATN $atn,
83*37748cd8SNickeau        array $decisionToDFA,
84*37748cd8SNickeau        PredictionContextCache $sharedContextCache
85*37748cd8SNickeau    ) {
86*37748cd8SNickeau        parent::__construct($atn, $sharedContextCache);
87*37748cd8SNickeau
88*37748cd8SNickeau        $this->decisionToDFA = $decisionToDFA;
89*37748cd8SNickeau        $this->recog = $recog;
90*37748cd8SNickeau        $this->prevAccept = new SimState();
91*37748cd8SNickeau    }
92*37748cd8SNickeau
93*37748cd8SNickeau    public function copyState(LexerATNSimulator $simulator) : void
94*37748cd8SNickeau    {
95*37748cd8SNickeau        $this->charPositionInLine = $simulator->charPositionInLine;
96*37748cd8SNickeau        $this->line = $simulator->line;
97*37748cd8SNickeau        $this->mode = $simulator->mode;
98*37748cd8SNickeau        $this->startIndex = $simulator->startIndex;
99*37748cd8SNickeau    }
100*37748cd8SNickeau
101*37748cd8SNickeau    public function getLine() : int
102*37748cd8SNickeau    {
103*37748cd8SNickeau        return $this->line;
104*37748cd8SNickeau    }
105*37748cd8SNickeau
106*37748cd8SNickeau    public function setLine(int $line) : void
107*37748cd8SNickeau    {
108*37748cd8SNickeau        $this->line = $line;
109*37748cd8SNickeau    }
110*37748cd8SNickeau
111*37748cd8SNickeau    public function getCharPositionInLine() : int
112*37748cd8SNickeau    {
113*37748cd8SNickeau        return $this->charPositionInLine;
114*37748cd8SNickeau    }
115*37748cd8SNickeau
116*37748cd8SNickeau    public function setCharPositionInLine(int $charPositionInLine) : void
117*37748cd8SNickeau    {
118*37748cd8SNickeau        $this->charPositionInLine = $charPositionInLine;
119*37748cd8SNickeau    }
120*37748cd8SNickeau
121*37748cd8SNickeau    public function match(CharStream $input, int $mode) : int
122*37748cd8SNickeau    {
123*37748cd8SNickeau        static $match_calls;
124*37748cd8SNickeau
125*37748cd8SNickeau        if ($match_calls === null) {
126*37748cd8SNickeau            $match_calls = 0;
127*37748cd8SNickeau        }
128*37748cd8SNickeau
129*37748cd8SNickeau        $match_calls++;
130*37748cd8SNickeau
131*37748cd8SNickeau        $this->mode = $mode;
132*37748cd8SNickeau        $mark = $input->mark();
133*37748cd8SNickeau
134*37748cd8SNickeau        try {
135*37748cd8SNickeau            $this->startIndex = $input->getIndex();
136*37748cd8SNickeau            $this->prevAccept->reset();
137*37748cd8SNickeau
138*37748cd8SNickeau            $dfa = $this->decisionToDFA[$mode];
139*37748cd8SNickeau
140*37748cd8SNickeau            if ($dfa->s0 === null) {
141*37748cd8SNickeau                return $this->matchATN($input);
142*37748cd8SNickeau            }
143*37748cd8SNickeau            return $this->execATN($input, $dfa->s0);
144*37748cd8SNickeau        } finally {
145*37748cd8SNickeau            $input->release($mark);
146*37748cd8SNickeau        }
147*37748cd8SNickeau    }
148*37748cd8SNickeau
149*37748cd8SNickeau    public function reset() : void
150*37748cd8SNickeau    {
151*37748cd8SNickeau        $this->prevAccept->reset();
152*37748cd8SNickeau        $this->startIndex = -1;
153*37748cd8SNickeau        $this->line = 1;
154*37748cd8SNickeau        $this->charPositionInLine = 0;
155*37748cd8SNickeau        $this->mode = Lexer::DEFAULT_MODE;
156*37748cd8SNickeau    }
157*37748cd8SNickeau
158*37748cd8SNickeau    protected function matchATN(CharStream $input) : int
159*37748cd8SNickeau    {
160*37748cd8SNickeau        $startState = $this->atn->modeToStartState[$this->mode];
161*37748cd8SNickeau
162*37748cd8SNickeau        if ($this->debug) {
163*37748cd8SNickeau            $this->log[] = \sprintf('matchATN mode %d start: %s', $this->mode, (string) $startState);
164*37748cd8SNickeau        }
165*37748cd8SNickeau
166*37748cd8SNickeau        $old_mode = $this->mode;
167*37748cd8SNickeau
168*37748cd8SNickeau        $s0_closure = $this->computeStartState($input, $startState);
169*37748cd8SNickeau        $suppressEdge = $s0_closure->hasSemanticContext;
170*37748cd8SNickeau        $s0_closure->hasSemanticContext = false;
171*37748cd8SNickeau
172*37748cd8SNickeau        $next = $this->addDFAState($s0_closure);
173*37748cd8SNickeau
174*37748cd8SNickeau        if (!$suppressEdge) {
175*37748cd8SNickeau            $this->decisionToDFA[$this->mode]->s0 = $next;
176*37748cd8SNickeau        }
177*37748cd8SNickeau
178*37748cd8SNickeau        $predict = $this->execATN($input, $next);
179*37748cd8SNickeau
180*37748cd8SNickeau        if ($this->debug) {
181*37748cd8SNickeau            $this->log[] = \sprintf('DFA after matchATN: %s', $this->decisionToDFA[$old_mode]->toLexerString());
182*37748cd8SNickeau        }
183*37748cd8SNickeau
184*37748cd8SNickeau        return $predict;
185*37748cd8SNickeau    }
186*37748cd8SNickeau
187*37748cd8SNickeau    protected function execATN(CharStream $input, DFAState $ds0) : int
188*37748cd8SNickeau    {
189*37748cd8SNickeau        if ($this->debug) {
190*37748cd8SNickeau            $this->log[] = \sprintf('start state closure=%s', (string) $ds0->configs);
191*37748cd8SNickeau        }
192*37748cd8SNickeau
193*37748cd8SNickeau        if ($ds0->isAcceptState) {
194*37748cd8SNickeau            // allow zero-length tokens
195*37748cd8SNickeau            $this->captureSimState($this->prevAccept, $input, $ds0);
196*37748cd8SNickeau        }
197*37748cd8SNickeau
198*37748cd8SNickeau        $t = $input->LA(1);
199*37748cd8SNickeau        $s = $ds0; // s is current/from DFA state
200*37748cd8SNickeau
201*37748cd8SNickeau        while (true) {
202*37748cd8SNickeau            if ($this->debug) {
203*37748cd8SNickeau                $this->log[] = \sprintf('execATN loop starting closure: %s', (string) $s->configs);
204*37748cd8SNickeau            }
205*37748cd8SNickeau
206*37748cd8SNickeau            // As we move src->trg, src->trg, we keep track of the previous trg to
207*37748cd8SNickeau            // avoid looking up the DFA state again, which is expensive.
208*37748cd8SNickeau            // If the previous target was already part of the DFA, we might
209*37748cd8SNickeau            // be able to avoid doing a reach operation upon t. If s!=null,
210*37748cd8SNickeau            // it means that semantic predicates didn't prevent us from
211*37748cd8SNickeau            // creating a DFA state. Once we know s!=null, we check to see if
212*37748cd8SNickeau            // the DFA state has an edge already for t. If so, we can just reuse
213*37748cd8SNickeau            // it's configuration set; there's no point in re-computing it.
214*37748cd8SNickeau            // This is kind of like doing DFA simulation within the ATN
215*37748cd8SNickeau            // simulation because DFA simulation is really just a way to avoid
216*37748cd8SNickeau            // computing reach/closure sets. Technically, once we know that
217*37748cd8SNickeau            // we have a previously added DFA state, we could jump over to
218*37748cd8SNickeau            // the DFA simulator. But, that would mean popping back and forth
219*37748cd8SNickeau            // a lot and making things more complicated algorithmically.
220*37748cd8SNickeau            // This optimization makes a lot of sense for loops within DFA.
221*37748cd8SNickeau            // A character will take us back to an existing DFA state
222*37748cd8SNickeau            // that already has lots of edges out of it. e.g., .* in comments.
223*37748cd8SNickeau            // print("Target for:" + str(s) + " and:" + str(t))
224*37748cd8SNickeau            $target = $this->getExistingTargetState($s, $t);
225*37748cd8SNickeau
226*37748cd8SNickeau            if ($target === null) {
227*37748cd8SNickeau                $target = $this->computeTargetState($input, $s, $t);
228*37748cd8SNickeau            }
229*37748cd8SNickeau
230*37748cd8SNickeau            if ($target === ATNSimulator::error()) {
231*37748cd8SNickeau                break;
232*37748cd8SNickeau            }
233*37748cd8SNickeau
234*37748cd8SNickeau            // If this is a consumable input element, make sure to consume before
235*37748cd8SNickeau            // capturing the accept state so the input index, line, and char
236*37748cd8SNickeau            // position accurately reflect the state of the interpreter at the
237*37748cd8SNickeau            // end of the token.
238*37748cd8SNickeau            if ($t !== Token::EOF) {
239*37748cd8SNickeau                $this->consume($input);
240*37748cd8SNickeau            }
241*37748cd8SNickeau
242*37748cd8SNickeau            if ($target->isAcceptState) {
243*37748cd8SNickeau                $this->captureSimState($this->prevAccept, $input, $target);
244*37748cd8SNickeau                if ($t === Token::EOF) {
245*37748cd8SNickeau                    break;
246*37748cd8SNickeau                }
247*37748cd8SNickeau            }
248*37748cd8SNickeau
249*37748cd8SNickeau            $t = $input->LA(1);
250*37748cd8SNickeau            $s = $target; // flip; current DFA target becomes new src/from state
251*37748cd8SNickeau        }
252*37748cd8SNickeau
253*37748cd8SNickeau        return $this->failOrAccept($this->prevAccept, $input, $s->configs, $t);
254*37748cd8SNickeau    }
255*37748cd8SNickeau
256*37748cd8SNickeau    /**
257*37748cd8SNickeau     * Get an existing target state for an edge in the DFA. If the target state
258*37748cd8SNickeau     * for the edge has not yet been computed or is otherwise not available,
259*37748cd8SNickeau     * this method returns `null`.
260*37748cd8SNickeau     *
261*37748cd8SNickeau     * @param DFAState $s The current DFA state
262*37748cd8SNickeau     * @param int      $t The next input symbol
263*37748cd8SNickeau     *
264*37748cd8SNickeau     * @return DFAState|null The existing target DFA state for the given input symbol
265*37748cd8SNickeau     *                       `t`, or `null` if the target state for this edg
266*37748cd8SNickeau     *                       is not already cached.
267*37748cd8SNickeau     */
268*37748cd8SNickeau    protected function getExistingTargetState(DFAState $s, int $t) : ?DFAState
269*37748cd8SNickeau    {
270*37748cd8SNickeau        if ($s->edges === null || $t < self::MIN_DFA_EDGE || $t > self::MAX_DFA_EDGE) {
271*37748cd8SNickeau            return null;
272*37748cd8SNickeau        }
273*37748cd8SNickeau
274*37748cd8SNickeau        $target = $s->edges[$t - self::MIN_DFA_EDGE] ?? null;
275*37748cd8SNickeau
276*37748cd8SNickeau        if ($this->debug && $target !== null) {
277*37748cd8SNickeau            $this->log[] = \sprintf('reuse state %d edge to %d', $s->stateNumber, $target->stateNumber);
278*37748cd8SNickeau        }
279*37748cd8SNickeau
280*37748cd8SNickeau        return $target;
281*37748cd8SNickeau    }
282*37748cd8SNickeau
283*37748cd8SNickeau    /**
284*37748cd8SNickeau     * Compute a target state for an edge in the DFA, and attempt to add the
285*37748cd8SNickeau     * computed state and corresponding edge to the DFA.
286*37748cd8SNickeau     *
287*37748cd8SNickeau     * @param CharStream $input The input stream
288*37748cd8SNickeau     * @param DFAState   $s     The current DFA state
289*37748cd8SNickeau     * @param int        $t     The next input symbol
290*37748cd8SNickeau     *
291*37748cd8SNickeau     * @return DFAState The computed target DFA state for the given input symbol
292*37748cd8SNickeau     *                  `t`. If `t` does not lead to a valid DFA state, this
293*37748cd8SNickeau     *                  method returns {@see LexerATNSimulator::ERROR}.
294*37748cd8SNickeau     */
295*37748cd8SNickeau    protected function computeTargetState(CharStream $input, DFAState $s, int $t) : DFAState
296*37748cd8SNickeau    {
297*37748cd8SNickeau        $reach = new OrderedATNConfigSet();
298*37748cd8SNickeau
299*37748cd8SNickeau        // if we don't find an existing DFA state
300*37748cd8SNickeau        // Fill reach starting from closure, following t transitions
301*37748cd8SNickeau        $this->getReachableConfigSet($input, $s->configs, $reach, $t);
302*37748cd8SNickeau
303*37748cd8SNickeau        if (\count($reach->elements()) === 0) {
304*37748cd8SNickeau            // we got nowhere on t from s
305*37748cd8SNickeau            if (!$reach->hasSemanticContext) {
306*37748cd8SNickeau                // we got nowhere on t, don't throw out this knowledge; it'd
307*37748cd8SNickeau                // cause a failover from DFA later.
308*37748cd8SNickeau                $this->addDFAEdge($s, $t, ATNSimulator::error());
309*37748cd8SNickeau            }
310*37748cd8SNickeau
311*37748cd8SNickeau            // stop when we can't match any more char
312*37748cd8SNickeau            return ATNSimulator::error();
313*37748cd8SNickeau        }
314*37748cd8SNickeau
315*37748cd8SNickeau        // Add an edge from s to target DFA found/created for reach
316*37748cd8SNickeau        return $this->addDFAEdgeATNConfigSet($s, $t, $reach) ?? ATNSimulator::error();
317*37748cd8SNickeau    }
318*37748cd8SNickeau
319*37748cd8SNickeau    protected function failOrAccept(SimState $prevAccept, CharStream $input, ATNConfigSet $reach, int $t) : int
320*37748cd8SNickeau    {
321*37748cd8SNickeau        if ($this->prevAccept->getDfaState() !== null) {
322*37748cd8SNickeau            $dfaState = $prevAccept->getDfaState();
323*37748cd8SNickeau
324*37748cd8SNickeau            if ($dfaState === null) {
325*37748cd8SNickeau                throw new \RuntimeException('Unexpected null DFA State.');
326*37748cd8SNickeau            }
327*37748cd8SNickeau
328*37748cd8SNickeau            $lexerActionExecutor = $dfaState->lexerActionExecutor;
329*37748cd8SNickeau
330*37748cd8SNickeau            $this->accept(
331*37748cd8SNickeau                $input,
332*37748cd8SNickeau                $lexerActionExecutor,
333*37748cd8SNickeau                $this->startIndex,
334*37748cd8SNickeau                $prevAccept->getIndex(),
335*37748cd8SNickeau                $prevAccept->getLine(),
336*37748cd8SNickeau                $prevAccept->getCharPos()
337*37748cd8SNickeau            );
338*37748cd8SNickeau
339*37748cd8SNickeau            return $dfaState->prediction;
340*37748cd8SNickeau        }
341*37748cd8SNickeau
342*37748cd8SNickeau        // if no accept and EOF is first char, return EOF
343*37748cd8SNickeau        if ($t === IntStream::EOF && $input->getIndex() === $this->startIndex) {
344*37748cd8SNickeau            return Token::EOF;
345*37748cd8SNickeau        }
346*37748cd8SNickeau
347*37748cd8SNickeau        if ($this->recog === null) {
348*37748cd8SNickeau            throw new \RuntimeException('Unexpected null recognizer.');
349*37748cd8SNickeau        }
350*37748cd8SNickeau
351*37748cd8SNickeau        throw new LexerNoViableAltException($this->recog, $input, $this->startIndex, $reach);
352*37748cd8SNickeau    }
353*37748cd8SNickeau
354*37748cd8SNickeau    /**
355*37748cd8SNickeau     * Given a starting configuration set, figure out all ATN configurations
356*37748cd8SNickeau     * we can reach upon input `t`. Parameter `reach` is a return parameter.
357*37748cd8SNickeau     */
358*37748cd8SNickeau    protected function getReachableConfigSet(
359*37748cd8SNickeau        CharStream $input,
360*37748cd8SNickeau        ATNConfigSet $closure,
361*37748cd8SNickeau        ATNConfigSet $reach,
362*37748cd8SNickeau        int $t
363*37748cd8SNickeau    ) : void {
364*37748cd8SNickeau        // this is used to skip processing for configs which have a lower priority
365*37748cd8SNickeau        // than a config that already reached an accept state for the same rule
366*37748cd8SNickeau        $skipAlt = ATN::INVALID_ALT_NUMBER;
367*37748cd8SNickeau
368*37748cd8SNickeau        foreach ($closure->elements() as $cfg) {
369*37748cd8SNickeau            if (!$cfg instanceof LexerATNConfig) {
370*37748cd8SNickeau                throw new \RuntimeException('Unexpected config type.');
371*37748cd8SNickeau            }
372*37748cd8SNickeau
373*37748cd8SNickeau            $currentAltReachedAcceptState = ($cfg->alt === $skipAlt);
374*37748cd8SNickeau
375*37748cd8SNickeau            if ($currentAltReachedAcceptState && $cfg->isPassedThroughNonGreedyDecision()) {
376*37748cd8SNickeau                continue;
377*37748cd8SNickeau            }
378*37748cd8SNickeau
379*37748cd8SNickeau            if ($this->debug) {
380*37748cd8SNickeau                $this->log[] = \sprintf(
381*37748cd8SNickeau                    "testing %s at %s\n",
382*37748cd8SNickeau                    $this->getTokenName($t),
383*37748cd8SNickeau                    $cfg->toString(true)
384*37748cd8SNickeau                );
385*37748cd8SNickeau            }
386*37748cd8SNickeau
387*37748cd8SNickeau            foreach ($cfg->state->getTransitions() as $trans) {
388*37748cd8SNickeau                $target = $this->getReachableTarget($trans, $t);
389*37748cd8SNickeau
390*37748cd8SNickeau                if ($target !== null) {
391*37748cd8SNickeau                    $lexerExecutor = $cfg->getLexerActionExecutor();
392*37748cd8SNickeau
393*37748cd8SNickeau                    if ($lexerExecutor !== null) {
394*37748cd8SNickeau                        $lexerExecutor = $lexerExecutor->fixOffsetBeforeMatch($input->getIndex() - $this->startIndex);
395*37748cd8SNickeau                    }
396*37748cd8SNickeau
397*37748cd8SNickeau                    $treatEofAsEpsilon = ($t === Token::EOF);
398*37748cd8SNickeau                    $config = new LexerATNConfig($cfg, $target, null, $lexerExecutor);
399*37748cd8SNickeau
400*37748cd8SNickeau                    if ($this->closure(
401*37748cd8SNickeau                        $input,
402*37748cd8SNickeau                        $config,
403*37748cd8SNickeau                        $reach,
404*37748cd8SNickeau                        $currentAltReachedAcceptState,
405*37748cd8SNickeau                        true,
406*37748cd8SNickeau                        $treatEofAsEpsilon
407*37748cd8SNickeau                    )) {
408*37748cd8SNickeau                        // any remaining configs for this alt have a lower priority
409*37748cd8SNickeau                        // than the one that just reached an accept state.
410*37748cd8SNickeau                        $skipAlt = $cfg->alt;
411*37748cd8SNickeau                    }
412*37748cd8SNickeau                }
413*37748cd8SNickeau            }
414*37748cd8SNickeau        }
415*37748cd8SNickeau    }
416*37748cd8SNickeau
417*37748cd8SNickeau    protected function accept(
418*37748cd8SNickeau        CharStream $input,
419*37748cd8SNickeau        ?LexerActionExecutor $lexerActionExecutor,
420*37748cd8SNickeau        int $startIndex,
421*37748cd8SNickeau        int $index,
422*37748cd8SNickeau        int $line,
423*37748cd8SNickeau        int $charPos
424*37748cd8SNickeau    ) : void {
425*37748cd8SNickeau        if ($this->debug) {
426*37748cd8SNickeau            $this->log[] = \sprintf('ACTION %s', (string) $lexerActionExecutor) . \PHP_EOL;
427*37748cd8SNickeau        }
428*37748cd8SNickeau
429*37748cd8SNickeau        // seek to after last char in token
430*37748cd8SNickeau        $input->seek($index);
431*37748cd8SNickeau        $this->line = $line;
432*37748cd8SNickeau        $this->charPositionInLine = $charPos;
433*37748cd8SNickeau
434*37748cd8SNickeau        if ($lexerActionExecutor !== null && $this->recog) {
435*37748cd8SNickeau            $lexerActionExecutor->execute($this->recog, $input, $startIndex);
436*37748cd8SNickeau        }
437*37748cd8SNickeau    }
438*37748cd8SNickeau
439*37748cd8SNickeau    protected function getReachableTarget(Transition $trans, int $t) : ?ATNState
440*37748cd8SNickeau    {
441*37748cd8SNickeau        if ($trans->matches($t, Lexer::MIN_CHAR_VALUE, Lexer::MAX_CHAR_VALUE)) {
442*37748cd8SNickeau            return $trans->target;
443*37748cd8SNickeau        }
444*37748cd8SNickeau
445*37748cd8SNickeau        return null;
446*37748cd8SNickeau    }
447*37748cd8SNickeau
448*37748cd8SNickeau    protected function computeStartState(CharStream $input, ATNState $p) : OrderedATNConfigSet
449*37748cd8SNickeau    {
450*37748cd8SNickeau        $initialContext = PredictionContext::empty();
451*37748cd8SNickeau        $configs = new OrderedATNConfigSet();
452*37748cd8SNickeau
453*37748cd8SNickeau        foreach ($p->getTransitions() as $i => $t) {
454*37748cd8SNickeau            $target = $t->target;
455*37748cd8SNickeau            $cfg = new LexerATNConfig(null, $target, $initialContext, null, $i + 1);
456*37748cd8SNickeau            $this->closure($input, $cfg, $configs, false, false, false);
457*37748cd8SNickeau        }
458*37748cd8SNickeau
459*37748cd8SNickeau        return $configs;
460*37748cd8SNickeau    }
461*37748cd8SNickeau
462*37748cd8SNickeau    /**
463*37748cd8SNickeau     * Since the alternatives within any lexer decision are ordered by
464*37748cd8SNickeau     * preference, this method stops pursuing the closure as soon as an accept
465*37748cd8SNickeau     * state is reached. After the first accept state is reached by depth-first
466*37748cd8SNickeau     * search from `config`, all other (potentially reachable) states for
467*37748cd8SNickeau     * this rule would have a lower priority.
468*37748cd8SNickeau     *
469*37748cd8SNickeau     * @return bool `true` if an accept state is reached, otherwise `false`.
470*37748cd8SNickeau     */
471*37748cd8SNickeau    protected function closure(
472*37748cd8SNickeau        CharStream $input,
473*37748cd8SNickeau        LexerATNConfig $config,
474*37748cd8SNickeau        ATNConfigSet $configs,
475*37748cd8SNickeau        bool $currentAltReachedAcceptState,
476*37748cd8SNickeau        bool $speculative,
477*37748cd8SNickeau        bool $treatEofAsEpsilon
478*37748cd8SNickeau    ) : bool {
479*37748cd8SNickeau        $cfg = null;
480*37748cd8SNickeau
481*37748cd8SNickeau        if ($this->debug) {
482*37748cd8SNickeau            $this->log[] = \sprintf('closure(%s)', $config->toString(true));
483*37748cd8SNickeau        }
484*37748cd8SNickeau
485*37748cd8SNickeau        if ($config->state instanceof States\RuleStopState) {
486*37748cd8SNickeau            if ($this->debug) {
487*37748cd8SNickeau                if ($this->recog) {
488*37748cd8SNickeau                    $this->log[] = \sprintf(
489*37748cd8SNickeau                        "closure at %s rule stop %s\n",
490*37748cd8SNickeau                        $this->recog->getRuleNames()[$config->state->ruleIndex],
491*37748cd8SNickeau                        $config
492*37748cd8SNickeau                    );
493*37748cd8SNickeau                } else {
494*37748cd8SNickeau                    $this->log[] = \sprintf("closure at rule stop %s\n", $config);
495*37748cd8SNickeau                }
496*37748cd8SNickeau            }
497*37748cd8SNickeau
498*37748cd8SNickeau            if ($config->context === null || $config->context->hasEmptyPath()) {
499*37748cd8SNickeau                if ($config->context === null || $config->context->isEmpty()) {
500*37748cd8SNickeau                    $configs->add($config);
501*37748cd8SNickeau
502*37748cd8SNickeau                    return true;
503*37748cd8SNickeau                }
504*37748cd8SNickeau
505*37748cd8SNickeau                $configs->add(new LexerATNConfig($config, $config->state, PredictionContext::empty()));
506*37748cd8SNickeau                $currentAltReachedAcceptState = true;
507*37748cd8SNickeau            }
508*37748cd8SNickeau
509*37748cd8SNickeau            if ($config->context !== null && !$config->context->isEmpty()) {
510*37748cd8SNickeau                for ($i = 0; $i < $config->context->getLength(); $i++) {
511*37748cd8SNickeau                    if ($config->context->getReturnState($i) !== PredictionContext::EMPTY_RETURN_STATE) {
512*37748cd8SNickeau                        $newContext = $config->context->getParent($i);// "pop" return state
513*37748cd8SNickeau                        $returnState = $this->atn->states[$config->context->getReturnState($i)];
514*37748cd8SNickeau                        $cfg = new LexerATNConfig($config, $returnState, $newContext);
515*37748cd8SNickeau                        $currentAltReachedAcceptState = $this->closure(
516*37748cd8SNickeau                            $input,
517*37748cd8SNickeau                            $cfg,
518*37748cd8SNickeau                            $configs,
519*37748cd8SNickeau                            $currentAltReachedAcceptState,
520*37748cd8SNickeau                            $speculative,
521*37748cd8SNickeau                            $treatEofAsEpsilon
522*37748cd8SNickeau                        );
523*37748cd8SNickeau                    }
524*37748cd8SNickeau                }
525*37748cd8SNickeau            }
526*37748cd8SNickeau
527*37748cd8SNickeau            return $currentAltReachedAcceptState;
528*37748cd8SNickeau        }
529*37748cd8SNickeau
530*37748cd8SNickeau        // optimization
531*37748cd8SNickeau        if (!$config->state->epsilonOnlyTransitions) {
532*37748cd8SNickeau            if (!$currentAltReachedAcceptState || !$config->isPassedThroughNonGreedyDecision()) {
533*37748cd8SNickeau                $configs->add($config);
534*37748cd8SNickeau            }
535*37748cd8SNickeau        }
536*37748cd8SNickeau
537*37748cd8SNickeau        foreach ($config->state->getTransitions() as $trans) {
538*37748cd8SNickeau            $cfg = $this->getEpsilonTarget($input, $config, $trans, $configs, $speculative, $treatEofAsEpsilon);
539*37748cd8SNickeau
540*37748cd8SNickeau            if ($cfg !== null) {
541*37748cd8SNickeau                $currentAltReachedAcceptState = $this->closure(
542*37748cd8SNickeau                    $input,
543*37748cd8SNickeau                    $cfg,
544*37748cd8SNickeau                    $configs,
545*37748cd8SNickeau                    $currentAltReachedAcceptState,
546*37748cd8SNickeau                    $speculative,
547*37748cd8SNickeau                    $treatEofAsEpsilon
548*37748cd8SNickeau                );
549*37748cd8SNickeau            }
550*37748cd8SNickeau        }
551*37748cd8SNickeau
552*37748cd8SNickeau        return $currentAltReachedAcceptState;
553*37748cd8SNickeau    }
554*37748cd8SNickeau
555*37748cd8SNickeau    /**
556*37748cd8SNickeau     * side-effect: can alter configs.hasSemanticContext
557*37748cd8SNickeau     */
558*37748cd8SNickeau    protected function getEpsilonTarget(
559*37748cd8SNickeau        CharStream $input,
560*37748cd8SNickeau        LexerATNConfig $config,
561*37748cd8SNickeau        Transition $t,
562*37748cd8SNickeau        ATNConfigSet $configs,
563*37748cd8SNickeau        bool $speculative,
564*37748cd8SNickeau        bool $treatEofAsEpsilon
565*37748cd8SNickeau    ) : ?LexerATNConfig {
566*37748cd8SNickeau        $cfg = null;
567*37748cd8SNickeau
568*37748cd8SNickeau        switch ($t->getSerializationType()) {
569*37748cd8SNickeau            case Transition::RULE:
570*37748cd8SNickeau                if (!$t instanceof RuleTransition) {
571*37748cd8SNickeau                    throw new \RuntimeException('Unexpected transition type.');
572*37748cd8SNickeau                }
573*37748cd8SNickeau
574*37748cd8SNickeau                $newContext = SingletonPredictionContext::create($config->context, $t->followState->stateNumber);
575*37748cd8SNickeau                $cfg = new LexerATNConfig($config, $t->target, $newContext);
576*37748cd8SNickeau
577*37748cd8SNickeau                break;
578*37748cd8SNickeau
579*37748cd8SNickeau            case Transition::PRECEDENCE:
580*37748cd8SNickeau                throw new \RuntimeException('Precedence predicates are not supported in lexers.');
581*37748cd8SNickeau
582*37748cd8SNickeau            case Transition::PREDICATE:
583*37748cd8SNickeau                // Track traversing semantic predicates. If we traverse,
584*37748cd8SNickeau                // we cannot add a DFA state for this "reach" computation
585*37748cd8SNickeau                // because the DFA would not test the predicate again in the
586*37748cd8SNickeau                // future. Rather than creating collections of semantic predicates
587*37748cd8SNickeau                // like v3 and testing them on prediction, v4 will test them on the
588*37748cd8SNickeau                // fly all the time using the ATN not the DFA. This is slower but
589*37748cd8SNickeau                // semantically it's not used that often. One of the key elements to
590*37748cd8SNickeau                // this predicate mechanism is not adding DFA states that see
591*37748cd8SNickeau                // predicates immediately afterwards in the ATN. For example,
592*37748cd8SNickeau
593*37748cd8SNickeau                // a : ID {p1}? | ID {p2}? ;
594*37748cd8SNickeau
595*37748cd8SNickeau                // should create the start state for rule 'a' (to save start state
596*37748cd8SNickeau                // competition), but should not create target of ID state. The
597*37748cd8SNickeau                // collection of ATN states the following ID references includes
598*37748cd8SNickeau                // states reached by traversing predicates. Since this is when we
599*37748cd8SNickeau                // test them, we cannot cash the DFA state target of ID.
600*37748cd8SNickeau
601*37748cd8SNickeau                if (!$t instanceof PredicateTransition) {
602*37748cd8SNickeau                    throw new \RuntimeException('Unexpected transition type.');
603*37748cd8SNickeau                }
604*37748cd8SNickeau
605*37748cd8SNickeau                if ($this->debug) {
606*37748cd8SNickeau                    $this->log[] = \sprintf('EVAL rule %d:%d', $t->ruleIndex, $t->predIndex);
607*37748cd8SNickeau                }
608*37748cd8SNickeau
609*37748cd8SNickeau                $configs->hasSemanticContext = true;
610*37748cd8SNickeau
611*37748cd8SNickeau                if ($this->evaluatePredicate($input, $t->ruleIndex, $t->predIndex, $speculative)) {
612*37748cd8SNickeau                    $cfg = new LexerATNConfig($config, $t->target);
613*37748cd8SNickeau                }
614*37748cd8SNickeau
615*37748cd8SNickeau                break;
616*37748cd8SNickeau
617*37748cd8SNickeau            case Transition::ACTION:
618*37748cd8SNickeau                if ($config->context === null || $config->context->hasEmptyPath()) {
619*37748cd8SNickeau                    // execute actions anywhere in the start rule for a token.
620*37748cd8SNickeau
621*37748cd8SNickeau                    // TODO: if the entry rule is invoked recursively, some
622*37748cd8SNickeau                    // actions may be executed during the recursive call. The
623*37748cd8SNickeau                    // problem can appear when hasEmptyPath() is true but
624*37748cd8SNickeau                    // isEmpty() is false. In this case, the config needs to be
625*37748cd8SNickeau                    // split into two contexts - one with just the empty path
626*37748cd8SNickeau                    // and another with everything but the empty path.
627*37748cd8SNickeau                    // Unfortunately, the current algorithm does not allow
628*37748cd8SNickeau                    // getEpsilonTarget to return two configurations, so
629*37748cd8SNickeau                    // additional modifications are needed before we can support
630*37748cd8SNickeau                    // the split operation.
631*37748cd8SNickeau
632*37748cd8SNickeau                    if (!$t instanceof ActionTransition) {
633*37748cd8SNickeau                        throw new \RuntimeException('Unexpected transition type.');
634*37748cd8SNickeau                    }
635*37748cd8SNickeau
636*37748cd8SNickeau                    $lexerAction = $this->atn->lexerActions[$t->actionIndex];
637*37748cd8SNickeau
638*37748cd8SNickeau                    $lexerActionExecutor = LexerActionExecutor::append($config->getLexerActionExecutor(), $lexerAction);
639*37748cd8SNickeau
640*37748cd8SNickeau                    $cfg = new LexerATNConfig($config, $t->target, null, $lexerActionExecutor);
641*37748cd8SNickeau                } else {
642*37748cd8SNickeau                    // ignore actions in referenced rules
643*37748cd8SNickeau                    $cfg = new LexerATNConfig($config, $t->target);
644*37748cd8SNickeau                }
645*37748cd8SNickeau
646*37748cd8SNickeau                break;
647*37748cd8SNickeau
648*37748cd8SNickeau            case Transition::EPSILON:
649*37748cd8SNickeau                $cfg = new LexerATNConfig($config, $t->target);
650*37748cd8SNickeau
651*37748cd8SNickeau                break;
652*37748cd8SNickeau
653*37748cd8SNickeau            case Transition::ATOM:
654*37748cd8SNickeau            case Transition::RANGE:
655*37748cd8SNickeau            case Transition::SET:
656*37748cd8SNickeau                if ($treatEofAsEpsilon) {
657*37748cd8SNickeau                    if ($t->matches(Token::EOF, 0, Lexer::MAX_CHAR_VALUE)) {
658*37748cd8SNickeau                        $cfg = new LexerATNConfig($config, $t->target);
659*37748cd8SNickeau                    }
660*37748cd8SNickeau                }
661*37748cd8SNickeau
662*37748cd8SNickeau                break;
663*37748cd8SNickeau
664*37748cd8SNickeau            default:
665*37748cd8SNickeau                $cfg = null;
666*37748cd8SNickeau        }
667*37748cd8SNickeau
668*37748cd8SNickeau        return $cfg;
669*37748cd8SNickeau    }
670*37748cd8SNickeau
671*37748cd8SNickeau    /**
672*37748cd8SNickeau     * Evaluate a predicate specified in the lexer.
673*37748cd8SNickeau     *
674*37748cd8SNickeau     * If `speculative` is `true`, this method was called before
675*37748cd8SNickeau     * {@see LexerATNSimulator::consume()} for the matched character. This
676*37748cd8SNickeau     * method should call {@see LexerATNSimulator::consume()} before evaluating
677*37748cd8SNickeau     * the predicate to ensure position sensitive values, including
678*37748cd8SNickeau     * {@see Lexer::getText()}, {@see Lexer::getLine()}, and
679*37748cd8SNickeau     * {@see Lexer::getCharPositionInLine()}, properly reflect the current
680*37748cd8SNickeau     * lexer state. This method should restore `input` and the simulator
681*37748cd8SNickeau     * to the original state before returning (i.e. undo the actions made by
682*37748cd8SNickeau     * the call to {@see LexerATNSimulator::consume()}.
683*37748cd8SNickeau     *
684*37748cd8SNickeau     * @param CharStream $input       The input stream.
685*37748cd8SNickeau     * @param int        $ruleIndex   The rule containing the predicate.
686*37748cd8SNickeau     * @param int        $predIndex   The index of the predicate within the rule.
687*37748cd8SNickeau     * @param bool       $speculative `true` if the current index in `input` is
688*37748cd8SNickeau     *                                one character before the predicate's location.
689*37748cd8SNickeau     *
690*37748cd8SNickeau     * @return bool `true` If the specified predicate evaluates to `true`.
691*37748cd8SNickeau     */
692*37748cd8SNickeau    protected function evaluatePredicate(CharStream $input, int $ruleIndex, int $predIndex, bool $speculative) : bool
693*37748cd8SNickeau    {
694*37748cd8SNickeau        if ($this->recog === null) {
695*37748cd8SNickeau            return true;
696*37748cd8SNickeau        }
697*37748cd8SNickeau
698*37748cd8SNickeau        if (!$speculative) {
699*37748cd8SNickeau            return $this->recog->sempred(null, $ruleIndex, $predIndex);
700*37748cd8SNickeau        }
701*37748cd8SNickeau
702*37748cd8SNickeau        $savedcolumn = $this->charPositionInLine;
703*37748cd8SNickeau        $savedLine = $this->line;
704*37748cd8SNickeau        $index = $input->getIndex();
705*37748cd8SNickeau        $marker = $input->mark();
706*37748cd8SNickeau
707*37748cd8SNickeau        try {
708*37748cd8SNickeau            $this->consume($input);
709*37748cd8SNickeau
710*37748cd8SNickeau            return $this->recog->sempred(null, $ruleIndex, $predIndex);
711*37748cd8SNickeau        } finally {
712*37748cd8SNickeau            $this->charPositionInLine = $savedcolumn;
713*37748cd8SNickeau            $this->line = $savedLine;
714*37748cd8SNickeau            $input->seek($index);
715*37748cd8SNickeau            $input->release($marker);
716*37748cd8SNickeau        }
717*37748cd8SNickeau    }
718*37748cd8SNickeau
719*37748cd8SNickeau    protected function captureSimState(SimState $settings, CharStream $input, DFAState $dfaState) : void
720*37748cd8SNickeau    {
721*37748cd8SNickeau        $settings->setIndex($input->getIndex());
722*37748cd8SNickeau        $settings->setLine($this->line);
723*37748cd8SNickeau        $settings->setCharPos($this->charPositionInLine);
724*37748cd8SNickeau        $settings->setDfaState($dfaState);
725*37748cd8SNickeau    }
726*37748cd8SNickeau
727*37748cd8SNickeau    protected function addDFAEdgeATNConfigSet(DFAState $from, int $t, ATNConfigSet $configs) : DFAState
728*37748cd8SNickeau    {
729*37748cd8SNickeau        /* leading to this call, ATNConfigSet.hasSemanticContext is used as a
730*37748cd8SNickeau         * marker indicating dynamic predicate evaluation makes this edge
731*37748cd8SNickeau         * dependent on the specific input sequence, so the static edge in the
732*37748cd8SNickeau         * DFA should be omitted. The target DFAState is still created since
733*37748cd8SNickeau         * execATN has the ability to resynchronize with the DFA state cache
734*37748cd8SNickeau         * following the predicate evaluation step.
735*37748cd8SNickeau         *
736*37748cd8SNickeau         * TJP notes: next time through the DFA, we see a pred again and eval.
737*37748cd8SNickeau         * If that gets us to a previously created (but dangling) DFA
738*37748cd8SNickeau         * state, we can continue in pure DFA mode from there.
739*37748cd8SNickeau         */
740*37748cd8SNickeau        $suppressEdge = $configs->hasSemanticContext;
741*37748cd8SNickeau        $configs->hasSemanticContext = false;
742*37748cd8SNickeau
743*37748cd8SNickeau        $to = $this->addDFAState($configs);
744*37748cd8SNickeau
745*37748cd8SNickeau        if ($suppressEdge) {
746*37748cd8SNickeau            return $to;
747*37748cd8SNickeau        }
748*37748cd8SNickeau
749*37748cd8SNickeau        $this->addDFAEdge($from, $t, $to);
750*37748cd8SNickeau
751*37748cd8SNickeau        return $to;
752*37748cd8SNickeau    }
753*37748cd8SNickeau
754*37748cd8SNickeau    protected function addDFAEdge(DFAState $from, int $t, ?DFAState $to) : void
755*37748cd8SNickeau    {
756*37748cd8SNickeau        // add the edge
757*37748cd8SNickeau        if ($t < self::MIN_DFA_EDGE || $t > self::MAX_DFA_EDGE) {
758*37748cd8SNickeau            // Only track edges within the DFA bounds
759*37748cd8SNickeau            return;
760*37748cd8SNickeau        }
761*37748cd8SNickeau
762*37748cd8SNickeau        if ($this->debug) {
763*37748cd8SNickeau            $this->log[] = \sprintf('EDGE %s->%s upon %d', $from, $to, $t);
764*37748cd8SNickeau        }
765*37748cd8SNickeau
766*37748cd8SNickeau        if ($from->edges === null) {
767*37748cd8SNickeau            // make room for tokens 1..n and -1 masquerading as index 0
768*37748cd8SNickeau            $from->edges = new \SplFixedArray(self::MAX_DFA_EDGE - self::MIN_DFA_EDGE + 1);
769*37748cd8SNickeau        }
770*37748cd8SNickeau
771*37748cd8SNickeau        $from->edges[$t - self::MIN_DFA_EDGE] = $to; // connect
772*37748cd8SNickeau    }
773*37748cd8SNickeau
774*37748cd8SNickeau    /**
775*37748cd8SNickeau     * Add a new DFA state if there isn't one with this set of configurations
776*37748cd8SNickeau     * already. This method also detects the first configuration containing
777*37748cd8SNickeau     * an ATN rule stop state. Later, when traversing the DFA, we will know
778*37748cd8SNickeau     * which rule to accept.
779*37748cd8SNickeau     */
780*37748cd8SNickeau    protected function addDFAState(ATNConfigSet $configs) : DFAState
781*37748cd8SNickeau    {
782*37748cd8SNickeau        if ($configs->hasSemanticContext) {
783*37748cd8SNickeau            throw new \RuntimeException('ATN Config Set cannot have semantic context.');
784*37748cd8SNickeau        }
785*37748cd8SNickeau
786*37748cd8SNickeau        $proposed = new DFAState($configs);
787*37748cd8SNickeau
788*37748cd8SNickeau        $firstConfigWithRuleStopState = null;
789*37748cd8SNickeau
790*37748cd8SNickeau        foreach ($configs->elements() as $config) {
791*37748cd8SNickeau            if ($config->state instanceof RuleStopState) {
792*37748cd8SNickeau                $firstConfigWithRuleStopState = $config;
793*37748cd8SNickeau
794*37748cd8SNickeau                break;
795*37748cd8SNickeau            }
796*37748cd8SNickeau        }
797*37748cd8SNickeau
798*37748cd8SNickeau        if ($firstConfigWithRuleStopState !== null) {
799*37748cd8SNickeau            if (!$firstConfigWithRuleStopState instanceof LexerATNConfig) {
800*37748cd8SNickeau                throw new \RuntimeException('Unexpected ATN config type.');
801*37748cd8SNickeau            }
802*37748cd8SNickeau
803*37748cd8SNickeau            $proposed->isAcceptState = true;
804*37748cd8SNickeau            $proposed->lexerActionExecutor = $firstConfigWithRuleStopState->getLexerActionExecutor();
805*37748cd8SNickeau
806*37748cd8SNickeau            $prediction = $this->atn->ruleToTokenType[$firstConfigWithRuleStopState->state->ruleIndex];
807*37748cd8SNickeau
808*37748cd8SNickeau            $proposed->prediction = $prediction ?? 0;
809*37748cd8SNickeau        }
810*37748cd8SNickeau
811*37748cd8SNickeau        $dfa = $this->decisionToDFA[$this->mode];
812*37748cd8SNickeau
813*37748cd8SNickeau        $existing = $dfa->states->get($proposed);
814*37748cd8SNickeau
815*37748cd8SNickeau        if ($existing !== null && $existing instanceof DFAState) {
816*37748cd8SNickeau            return $existing;
817*37748cd8SNickeau        }
818*37748cd8SNickeau
819*37748cd8SNickeau        $newState = $proposed;
820*37748cd8SNickeau        $newState->stateNumber = $dfa->states->count();
821*37748cd8SNickeau        $configs->setReadonly(true);
822*37748cd8SNickeau        $newState->configs = $configs;
823*37748cd8SNickeau        $dfa->states->add($newState);
824*37748cd8SNickeau
825*37748cd8SNickeau        return $newState;
826*37748cd8SNickeau    }
827*37748cd8SNickeau
828*37748cd8SNickeau    public function getDFA(int $mode) : DFA
829*37748cd8SNickeau    {
830*37748cd8SNickeau        return $this->decisionToDFA[$mode];
831*37748cd8SNickeau    }
832*37748cd8SNickeau
833*37748cd8SNickeau    /**
834*37748cd8SNickeau     * Get the text matched so far for the current token.
835*37748cd8SNickeau     */
836*37748cd8SNickeau    public function getText(CharStream $input) : string
837*37748cd8SNickeau    {
838*37748cd8SNickeau        // index is first lookahead char, don't include.
839*37748cd8SNickeau        return $input->getText($this->startIndex, $input->getIndex() - 1);
840*37748cd8SNickeau    }
841*37748cd8SNickeau
842*37748cd8SNickeau    public function consume(CharStream $input) : void
843*37748cd8SNickeau    {
844*37748cd8SNickeau        $curChar = $input->LA(1);
845*37748cd8SNickeau
846*37748cd8SNickeau        if ($curChar === self::NEW_LINE_CODE) {
847*37748cd8SNickeau            $this->line++;
848*37748cd8SNickeau            $this->charPositionInLine = 0;
849*37748cd8SNickeau        } else {
850*37748cd8SNickeau            $this->charPositionInLine++;
851*37748cd8SNickeau        }
852*37748cd8SNickeau
853*37748cd8SNickeau        $input->consume();
854*37748cd8SNickeau    }
855*37748cd8SNickeau
856*37748cd8SNickeau    public function getTokenName(int $t) : ?string
857*37748cd8SNickeau    {
858*37748cd8SNickeau        if ($t === -1) {
859*37748cd8SNickeau            return 'EOF';
860*37748cd8SNickeau        }
861*37748cd8SNickeau
862*37748cd8SNickeau        return \sprintf('\'%s\'', StringUtils::char($t));
863*37748cd8SNickeau    }
864*37748cd8SNickeau}
865