1<?php
2
3declare(strict_types=1);
4
5namespace Antlr\Antlr4\Runtime\Error\Exceptions;
6
7use Antlr\Antlr4\Runtime\IntervalSet;
8use Antlr\Antlr4\Runtime\IntStream;
9use Antlr\Antlr4\Runtime\ParserRuleContext;
10use Antlr\Antlr4\Runtime\Recognizer;
11use Antlr\Antlr4\Runtime\RuleContext;
12use Antlr\Antlr4\Runtime\Token;
13
14/**
15 * The root of the ANTLR exception hierarchy. In general, ANTLR tracks just
16 * 3 kinds of errors: prediction errors, failed predicate errors, and
17 * mismatched input errors. In each case, the parser knows where it is
18 * in the input, where it is in the ATN, the rule invocation stack,
19 * and what kind of problem occurred.
20 */
21class RecognitionException extends \RuntimeException
22{
23    /**
24     * The {@see Recognizer} where this exception originated.
25     *
26     * @var Recognizer|null
27     */
28    private $recognizer;
29
30    /** @var RuleContext|null */
31    private $ctx;
32
33    /** @var IntStream|null */
34    private $input;
35
36    /**
37     * The current {@see Token} when an error occurred. Since not all streams
38     * support accessing symbols by index, we have to track the {@see Token}
39     * instance itself.
40     *
41     * @var Token|null
42     */
43    private $offendingToken;
44
45    /** @var int */
46    private $offendingState = -1;
47
48    public function __construct(
49        ?Recognizer $recognizer,
50        ?IntStream $input,
51        ?ParserRuleContext $ctx,
52        string $message = ''
53    ) {
54        parent::__construct($message);
55
56        $this->recognizer = $recognizer;
57        $this->input = $input;
58        $this->ctx = $ctx;
59
60        if ($this->recognizer !== null) {
61            $this->offendingState = $this->recognizer->getState();
62        }
63    }
64
65    /**
66     * Get the ATN state number the parser was in at the time the error
67     * occurred. For {@see NoViableAltException} and
68     * {@see LexerNoViableAltException} exceptions, this is the
69     * {@see DecisionState} number. For others, it is the state whose outgoing
70     * edge we couldn't match.
71     *
72     * If the state number is not known, this method returns -1.
73     */
74    public function getOffendingState() : int
75    {
76        return $this->offendingState;
77    }
78
79    public function setOffendingState(int $offendingState) : void
80    {
81        $this->offendingState = $offendingState;
82    }
83
84    /**
85     * If the state number is not known, this method returns -1.
86     *
87     * Gets the set of input symbols which could potentially follow the
88     * previously matched symbol at the time this exception was thrown.
89     *
90     * If the set of expected tokens is not known and could not be computed,
91     * this method returns `null`.
92     *
93     * @return IntervalSet|null The set of token types that could potentially follow
94     *                          the current state in the ATN, or `null` if
95     *                          the information is not available.
96     */
97    public function getExpectedTokens() : ?IntervalSet
98    {
99        if ($this->recognizer === null) {
100            return null;
101        }
102
103        if ($this->ctx === null) {
104            throw new \RuntimeException('Unexpected null context.');
105        }
106
107        return $this->recognizer->getATN()->getExpectedTokens($this->offendingState, $this->ctx);
108    }
109
110    /**
111     * Gets the {@see RuleContext} at the time this exception was thrown.
112     *
113     * If the context is not available, this method returns `null`.
114     *
115     * @return RuleContext|null The {@see RuleContext} at the time this exception
116     *                          was thrown. If the context is not available, this
117     *                          method returns `null`.
118     */
119    public function getCtx() : ?RuleContext
120    {
121        return $this->ctx;
122    }
123
124    /**
125     * Gets the input stream which is the symbol source for the recognizer where
126     * this exception was thrown.
127     *
128     * If the input stream is not available, this method returns `null`.
129     *
130     * @return IntStream|null The input stream which is the symbol source for
131     *                        the recognizer where this exception was thrown, or
132     *                        `null` if the stream is not available.
133     */
134    public function getInputStream() : ?IntStream
135    {
136        return $this->input;
137    }
138
139    public function getOffendingToken() : ?Token
140    {
141        return $this->offendingToken;
142    }
143
144    public function setOffendingToken(?Token $offendingToken) : void
145    {
146        $this->offendingToken = $offendingToken;
147    }
148
149    /**
150     * Gets the {@see Recognizer} where this exception occurred.
151     *
152     * If the recognizer is not available, this method returns `null`.
153     *
154     * @return Recognizer|null The recognizer where this exception occurred, or
155     *                         `null` if the recognizer is not available.
156     */
157    public function getRecognizer() : ?Recognizer
158    {
159        return $this->recognizer;
160    }
161
162    public function __toString() : string
163    {
164        return $this->message;
165    }
166}
167