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