1<?php 2 3declare(strict_types=1); 4 5namespace Antlr\Antlr4\Runtime; 6 7use Antlr\Antlr4\Runtime\Utils\Pair; 8use Antlr\Antlr4\Runtime\Utils\StringUtils; 9 10final class CommonToken implements WritableToken 11{ 12 /** 13 * This is the backing field for {@see CommonToken::getType()} and 14 * {@see CommonToken::setType()}. 15 * 16 * @var int 17 */ 18 protected $type; 19 20 /** 21 * This is the backing field for {@see CommonToken::getLine()} and 22 * {@see CommonToken::setLine()}. 23 * 24 * @var int 25 */ 26 protected $line = 0; 27 28 /** 29 * This is the backing field for {@see CommonToken::getCharPositionInLine()} 30 * and {@see CommonToken::setCharPositionInLine()}. 31 * 32 * @var int 33 */ 34 protected $charPositionInLine = -1; 35 36 /** 37 * This is the backing field for {@see CommonToken::getChannel()} and 38 * {@see CommonToken::setChannel()}. 39 * 40 * @var int 41 */ 42 protected $channel = Token::DEFAULT_CHANNEL; 43 44 /** 45 * This is the backing field for {@see CommonToken::getTokenSource()} and 46 * {@see CommonToken::getInputStream()}. 47 * 48 * 49 * These properties share a field to reduce the memory footprint of 50 * {@see CommonToken}. Tokens created by a {@see CommonTokenFactory} from 51 * the same source and input stream share a reference to the same 52 * {@see Pair} containing these values. 53 * 54 * @var Pair 55 */ 56 protected $source; 57 58 /** 59 * This is the backing field for {@see CommonToken::getText()} when the token 60 * text is explicitly set in the constructor or via {@see CommonToken::setText()}. 61 * 62 * @see CommonToken::getText() 63 * 64 * @var string|null 65 */ 66 protected $text; 67 68 /** 69 * This is the backing field for {@see CommonToken::getTokenIndex()} and 70 * {@see CommonToken::setTokenIndex()}. 71 * 72 * @var int 73 */ 74 protected $index = -1; 75 76 /** 77 * This is the backing field for {@see CommonToken::getStartIndex()} and 78 * {@see CommonToken::setStartIndex()}. 79 * 80 * @var int 81 */ 82 protected $start; 83 84 /** 85 * This is the backing field for {@see CommonToken::getStopIndex()} and 86 * {@see CommonToken::setStopIndex()}. 87 * 88 * @var int 89 */ 90 protected $stop; 91 92 public function __construct( 93 int $type, 94 ?Pair $source = null, 95 ?int $channel = null, 96 int $start = -1, 97 int $stop = -1 98 ) { 99 if ($source !== null && !$source->a instanceof TokenSource) { 100 throw new \RuntimeException('Unexpected token source type.'); 101 } 102 103 if ($source !== null && !$source->b instanceof CharStream) { 104 throw new \RuntimeException('Unexpected stream type.'); 105 } 106 107 $this->source = $source ?? self::emptySource(); 108 $this->type = $type; 109 $this->channel = $channel ?? Token::DEFAULT_CHANNEL; 110 $this->start = $start; 111 $this->stop = $stop; 112 113 $tokenSource = $this->source->a; 114 115 if ($tokenSource instanceof TokenSource) { 116 $this->line = $tokenSource->getLine(); 117 $this->charPositionInLine = $tokenSource->getCharPositionInLine(); 118 } 119 } 120 121 /** 122 * An empty {@see Pair}, which is used as the default value of 123 * {@see CommonToken::source()} for tokens that do not have a source. 124 */ 125 public static function emptySource() : Pair 126 { 127 static $source; 128 129 return $source = $source ?? new Pair(null, null); 130 } 131 132 /** 133 * Constructs a new {@see CommonToken} as a copy of another {@see Token}. 134 * 135 * If `oldToken` is also a {@see CommonToken} instance, the newly constructed 136 * token will share a reference to the {@see CommonToken::text()} field and 137 * the {@see Pair} stored in {@see CommonToken::source()}. Otherwise, 138 * {@see CommonToken::text()} will be assigned the result of calling 139 * {@see CommonToken::getText()}, and {@see CommonToken::source()} will be 140 * constructed from the result of {@see Token::getTokenSource()} and 141 * {@see Token::getInputStream()}. 142 */ 143 public function clone() : CommonToken 144 { 145 $token = new self($this->type, $this->source, $this->channel, $this->start, $this->stop); 146 147 $token->index = $this->index; 148 $token->line = $this->line; 149 $token->charPositionInLine = $this->charPositionInLine; 150 151 $token->setText($this->text); 152 153 return $token; 154 } 155 156 public function getType() : int 157 { 158 return $this->type; 159 } 160 161 public function setType(int $type) : void 162 { 163 $this->type = $type; 164 } 165 166 public function getLine() : int 167 { 168 return $this->line; 169 } 170 171 public function setLine(int $line) : void 172 { 173 $this->line = $line; 174 } 175 176 public function getText() : ?string 177 { 178 if ($this->text !== null) { 179 return $this->text; 180 } 181 182 $input = $this->getInputStream(); 183 184 if ($input === null) { 185 return null; 186 } 187 188 $n = $input->getLength(); 189 190 if ($this->start < $n && $this->stop < $n) { 191 return $input->getText($this->start, $this->stop); 192 } 193 194 return '<EOF>'; 195 } 196 197 /** 198 * Explicitly set the text for this token. If `text` is not `null`, then 199 * {@see CommonToken::getText()} will return this value rather than 200 * extracting the text from the input. 201 * 202 * @param string $text The explicit text of the token, or `null` 203 * if the text should be obtained from the input 204 * along with the start and stop indexes of the token. 205 */ 206 public function setText(?string $text) : void 207 { 208 $this->text = $text; 209 } 210 211 public function getCharPositionInLine() : int 212 { 213 return $this->charPositionInLine; 214 } 215 216 public function setCharPositionInLine(int $charPositionInLine) : void 217 { 218 $this->charPositionInLine = $charPositionInLine; 219 } 220 221 public function getChannel() : int 222 { 223 return $this->channel; 224 } 225 226 public function setChannel(int $channel) : void 227 { 228 $this->channel = $channel; 229 } 230 231 public function getStartIndex() : int 232 { 233 return $this->start; 234 } 235 236 public function setStartIndex(int $index) : void 237 { 238 $this->start = $index; 239 } 240 241 public function getStopIndex() : int 242 { 243 return $this->stop; 244 } 245 246 public function setStopIndex(int $index) : void 247 { 248 $this->stop = $index; 249 } 250 251 public function getTokenIndex() : int 252 { 253 return $this->index; 254 } 255 256 public function setTokenIndex(int $tokenIndex) : void 257 { 258 $this->index = $tokenIndex; 259 } 260 261 public function getTokenSource() : ?TokenSource 262 { 263 $source = $this->source->a; 264 265 if ($source !== null && !$source instanceof TokenSource) { 266 throw new \RuntimeException('Unexpected token source type.'); 267 } 268 269 return $source; 270 } 271 272 public function getInputStream() : ?CharStream 273 { 274 $stream = $this->source->b; 275 276 if ($stream !== null && !$stream instanceof CharStream) { 277 throw new \RuntimeException('Unexpected token source type.'); 278 } 279 280 return $stream; 281 } 282 283 public function getSource() : Pair 284 { 285 return $this->source; 286 } 287 288 public function __toString() : string 289 { 290 return \sprintf( 291 '[@%d,%d:%d=\'%s\',<%d>%s,%d:%d]', 292 $this->getTokenIndex(), 293 $this->start, 294 $this->stop, 295 StringUtils::escapeWhitespace($this->getText() ?? ''), 296 $this->type, 297 $this->channel > 0 ? ',channel=' . $this->channel : '', 298 $this->line, 299 $this->charPositionInLine 300 ); 301 } 302} 303