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