1<?php
2
3declare(strict_types=1);
4
5namespace Antlr\Antlr4\Runtime;
6
7use Antlr\Antlr4\Runtime\Atn\ATN;
8use Antlr\Antlr4\Runtime\Atn\ATNSimulator;
9use Antlr\Antlr4\Runtime\Error\Exceptions\RecognitionException;
10use Antlr\Antlr4\Runtime\Error\Listeners\ANTLRErrorListener;
11use Antlr\Antlr4\Runtime\Error\Listeners\ProxyErrorListener;
12
13abstract class Recognizer
14{
15    public const EOF = -1;
16
17    /** @var array<string> */
18    public $log = [];
19
20    /** @var array<string, array<string, int>> */
21    private static $tokenTypeMapCache = [];
22
23    /** @var array<string, array<int, string>> */
24    private static $ruleIndexMapCache = [];
25
26    /** @var array<ANTLRErrorListener> */
27    private $listeners;
28
29    /** @var ATNSimulator|null */
30    protected $interp;
31
32    /** @var int */
33    private $stateNumber = -1;
34
35    public function __construct()
36    {
37        $this->listeners = [];
38    }
39
40    /**
41     * Get the vocabulary used by the recognizer.
42     *
43     * @return Vocabulary A {@see Vocabulary} instance providing information
44     *                    about the vocabulary used by the grammar.
45     */
46    abstract public function getVocabulary() : Vocabulary;
47
48    /**
49     * Get a map from token names to token types.
50     *
51     * Used for XPath and tree pattern compilation.
52     *
53     * @return array<string, int>
54     */
55    public function getTokenTypeMap() : array
56    {
57        $vocabulary = $this->getVocabulary();
58
59        $key = \spl_object_hash($vocabulary);
60        $result = self::$tokenTypeMapCache[$key] ?? null;
61
62        if ($result === null) {
63            $result = [];
64
65            for ($i = 0; $i <= $this->getATN()->maxTokenType; $i++) {
66                $literalName = $vocabulary->getLiteralName($i);
67
68                if ($literalName !== null) {
69                    $result[$literalName] = $i;
70                }
71
72                $symbolicName = $vocabulary->getSymbolicName($i);
73
74                if ($symbolicName !== null) {
75                    $result[$symbolicName] = $i;
76                }
77            }
78
79            $result['EOF'] = Token::EOF;
80
81            self::$tokenTypeMapCache[$key] = $result;
82        }
83
84        return $result;
85    }
86
87    /**
88     * Get a map from rule names to rule indexes.
89     *
90     * Used for XPath and tree pattern compilation.
91     *
92     * @return array<int, string>|null
93     */
94    public function getRuleIndexMap() : ?array
95    {
96        $result = self::$ruleIndexMapCache[static::class] ?? null;
97
98        if ($result === null) {
99            self::$ruleIndexMapCache[static::class] = $this->getRuleNames();
100        }
101
102        return $result;
103    }
104
105    public function getTokenType(string $tokenName) : int
106    {
107        $map = $this->getTokenTypeMap();
108
109        return $map[$tokenName] ?? Token::INVALID_TYPE;
110    }
111
112    /**
113     * If this recognizer was generated, it will have a serialized ATN
114     * representation of the grammar. For interpreters, we don't know
115     * their serialized ATN despite having created the interpreter from it.
116     */
117    public function getSerializedATN() : string
118    {
119        throw new \InvalidArgumentException('there is no serialized ATN');
120    }
121
122    /**
123     * Get the ATN interpreter used by the recognizer for prediction.
124     *
125     * @return ATNSimulator|null The ATN interpreter used by the recognizer
126     *                           for prediction.
127     */
128    public function getInterpreter() : ?ATNSimulator
129    {
130        return $this->interp;
131    }
132
133    protected function interpreter() : ATNSimulator
134    {
135        if ($this->interp === null) {
136            throw new \RuntimeException('Unexpected null interpreter.');
137        }
138
139        return $this->interp;
140    }
141
142    /**
143     * Set the ATN interpreter used by the recognizer for prediction.
144     *
145     * @param ATNSimulator|null $interpreter The ATN interpreter used
146     *                                       by the recognizer for prediction.
147     */
148    public function setInterpreter(?ATNSimulator $interpreter) : void
149    {
150        $this->interp = $interpreter;
151    }
152
153    /**
154     * What is the error header, normally line/character position information?
155     */
156    public function getErrorHeader(RecognitionException $e) : string
157    {
158        $token = $e->getOffendingToken();
159
160        if ($token === null) {
161            return '';
162        }
163
164        return \sprintf('line %d:%d', $token->getLine(), $token->getCharPositionInLine());
165    }
166
167    public function addErrorListener(ANTLRErrorListener $listener) : void
168    {
169        $this->listeners[] = $listener;
170    }
171
172    public function removeErrorListeners() : void
173    {
174        $this->listeners = [];
175    }
176
177    public function getErrorListenerDispatch() : ANTLRErrorListener
178    {
179        return new ProxyErrorListener($this->listeners);
180    }
181
182    /**
183     * Subclass needs to override these if there are sempreds or actions
184     * that the ATN interp needs to execute
185     */
186    public function sempred(?RuleContext $localctx, int $ruleIndex, int $actionIndex) : bool
187    {
188        return true;
189    }
190
191    public function precpred(RuleContext $localctx, int $precedence) : bool
192    {
193        return true;
194    }
195
196    public function action(?RuleContext $localctx, int $ruleIndex, int $actionIndex) : void
197    {
198    }
199
200    public function getState() : int
201    {
202        return $this->stateNumber;
203    }
204
205    /**
206     * Indicate that the recognizer has changed internal state that is
207     * consistent with the ATN state passed in. This way we always know
208     * where we are in the ATN as the parser goes along. The rule
209     * context objects form a stack that lets us see the stack of
210     * invoking rules. Combine this and we have complete ATN
211     * configuration information.
212     */
213    public function setState(int $atnState) : void
214    {
215        $this->stateNumber = $atnState;
216    }
217
218    abstract public function getInputStream() : ?IntStream;
219    abstract public function setInputStream(IntStream $input) : void;
220    abstract public function getTokenFactory() : TokenFactory;
221    abstract public function setTokenFactory(TokenFactory $input) : void;
222
223    /**
224     * @return array<int, string>
225     */
226    abstract public function getRuleNames() : array;
227
228    /**
229     * Get the {@see ATN} used by the recognizer for prediction.
230     *
231     * @return ATN The {@see ATN} used by the recognizer for prediction.
232     */
233    abstract public function getATN() : ATN;
234}
235