1<?php 2 3declare(strict_types=1); 4 5namespace Antlr\Antlr4\Runtime; 6 7/** 8 * This class extends {@see BufferedTokenStream} with functionality to filter 9 * token streams to tokens on a particular channel (tokens where 10 * {@see Token::getChannel()} returns a particular value). 11 * 12 * This token stream provides access to all tokens by index or when calling 13 * methods like {@see CommonTokenStream::getText()}. The channel filtering 14 * is only used for code accessing tokens via the lookahead methods 15 * {@see CommonTokenStream::LA()}, {@see CommonTokenStream::LT()}, and 16 * {@see CommonTokenStream::LB()}. 17 * 18 * By default, tokens are placed on the default channel 19 * ({@see Token::DEFAULT_CHANNEL()}), but may be reassigned by using the 20 * {@code CommonTokenStream::channel(HIDDEN)} lexer command, or by using an 21 * embedded action to call {@see Lexer::setChannel()}. 22 * 23 * 24 * 25 * Note: lexer rules which use the `$this->skip` lexer command or call 26 * {@see Lexer::skip()} do not produce tokens at all, so input text matched by 27 * such a rule will not be available as part of the token stream, regardless of 28 * channel.we 29 */ 30final class CommonTokenStream extends BufferedTokenStream 31{ 32 /** 33 * Specifies the channel to use for filtering tokens. 34 * 35 * 36 * The default value is {@see Token::DEFAULT_CHANNEL}, which matches the 37 * default channel assigned to tokens created by the lexer. 38 * 39 * @var int 40 */ 41 protected $channel; 42 43 /** 44 * Constructs a new {@see CommonTokenStream} using the specified token 45 * source and filtering tokens to the specified channel. Only tokens whose 46 * {@see Token::getChannel()} matches `channel` or have the 47 * {@see Token::getType()} equal to {@see Token::EOF} will be returned by 48 * tthe oken stream lookahead methods. 49 * 50 * @param TokenSource $tokenSource The token source. 51 * @param int $channel The channel to use for filtering tokens. 52 */ 53 public function __construct(TokenSource $tokenSource, int $channel = Token::DEFAULT_CHANNEL) 54 { 55 parent::__construct($tokenSource); 56 57 $this->channel = $channel; 58 } 59 60 public function adjustSeekIndex(int $i) : int 61 { 62 return $this->nextTokenOnChannel($i, $this->channel); 63 } 64 65 protected function LB(int $k) : ?Token 66 { 67 if ($k === 0 || $this->index - $k < 0) { 68 return null; 69 } 70 71 // find k good tokens looking backwards 72 $i = $this->index; 73 $n = 1; 74 while ($n <= $k) { 75 // skip off-channel tokens 76 $i = $this->previousTokenOnChannel($i - 1, $this->channel); 77 $n++; 78 } 79 80 if ($i < 0) { 81 return null; 82 } 83 84 return $this->tokens[$i]; 85 } 86 87 public function LT(int $k) : ?Token 88 { 89 $this->lazyInit(); 90 91 if ($k === 0) { 92 return null; 93 } 94 95 if ($k < 0) { 96 return $this->LB(-$k); 97 } 98 99 // find k good tokens 100 $i = $this->index; 101 $n = 1; // we know tokens[pos] is a good one 102 while ($n < $k) { 103 // skip off-channel tokens, but make sure to not look past EOF 104 if ($this->sync($i + 1)) { 105 $i = $this->nextTokenOnChannel($i + 1, $this->channel); 106 } 107 108 $n++; 109 } 110 111 return $this->tokens[$i]; 112 } 113 114 /** 115 * Count EOF just once. 116 */ 117 public function getNumberOfOnChannelTokens() : int 118 { 119 $n = 0; 120 121 $this->fill(); 122 123 foreach ($this->tokens as $t) { 124 if ($t->getChannel() === $this->channel) { 125 $n++; 126 } 127 128 if ($t->getType() === Token::EOF) { 129 break; 130 } 131 } 132 133 return $n; 134 } 135} 136