UNPKG

6.94 kBJavaScriptView Raw
1import { TokenKind } from './Token';
2import { TokenSequence } from './TokenSequence';
3/**
4 * Manages a stream of tokens that are read by the parser.
5 *
6 * @remarks
7 * Use TokenReader.readToken() to read a token and advance the stream pointer.
8 * Use TokenReader.peekToken() to preview the next token.
9 * Use TokenReader.createMarker() and backtrackToMarker() to rewind to an earlier point.
10 * Whenever readToken() is called, the token is added to an accumulated TokenSequence
11 * that can be extracted by calling extractAccumulatedSequence().
12 */
13var TokenReader = /** @class */ (function () {
14 function TokenReader(parserContext, embeddedTokenSequence) {
15 this._parserContext = parserContext;
16 this.tokens = parserContext.tokens;
17 if (embeddedTokenSequence) {
18 if (embeddedTokenSequence.parserContext !== this._parserContext) {
19 throw new Error('The embeddedTokenSequence must use the same parser context');
20 }
21 this._readerStartIndex = embeddedTokenSequence.startIndex;
22 this._readerEndIndex = embeddedTokenSequence.endIndex;
23 }
24 else {
25 this._readerStartIndex = 0;
26 this._readerEndIndex = this.tokens.length;
27 }
28 this._currentIndex = this._readerStartIndex;
29 this._accumulatedStartIndex = this._readerStartIndex;
30 }
31 /**
32 * Extracts and returns the TokenSequence that was accumulated so far by calls to readToken().
33 * The next call to readToken() will start a new accumulated sequence.
34 */
35 TokenReader.prototype.extractAccumulatedSequence = function () {
36 if (this._accumulatedStartIndex === this._currentIndex) {
37 // If this happens, it indicates a parser bug:
38 throw new Error('Parser assertion failed: The queue should not be empty when' +
39 ' extractAccumulatedSequence() is called');
40 }
41 var sequence = new TokenSequence({
42 parserContext: this._parserContext,
43 startIndex: this._accumulatedStartIndex,
44 endIndex: this._currentIndex
45 });
46 this._accumulatedStartIndex = this._currentIndex;
47 return sequence;
48 };
49 /**
50 * Returns true if the accumulated sequence has any tokens yet. This will be false
51 * when the TokenReader starts, and it will be false immediately after a call
52 * to extractAccumulatedSequence(). Otherwise, it will become true whenever readToken()
53 * is called.
54 */
55 TokenReader.prototype.isAccumulatedSequenceEmpty = function () {
56 return this._accumulatedStartIndex === this._currentIndex;
57 };
58 /**
59 * Like extractAccumulatedSequence(), but returns undefined if nothing has been
60 * accumulated yet.
61 */
62 TokenReader.prototype.tryExtractAccumulatedSequence = function () {
63 if (this.isAccumulatedSequenceEmpty()) {
64 return undefined;
65 }
66 return this.extractAccumulatedSequence();
67 };
68 /**
69 * Asserts that isAccumulatedSequenceEmpty() should return false. If not, an exception
70 * is throw indicating a parser bug.
71 */
72 TokenReader.prototype.assertAccumulatedSequenceIsEmpty = function () {
73 if (!this.isAccumulatedSequenceEmpty()) {
74 // If this happens, it indicates a parser bug:
75 var sequence = new TokenSequence({
76 parserContext: this._parserContext,
77 startIndex: this._accumulatedStartIndex,
78 endIndex: this._currentIndex
79 });
80 var tokenStrings = sequence.tokens.map(function (x) { return x.toString(); });
81 throw new Error('Parser assertion failed: The queue should be empty, but it contains:\n' +
82 JSON.stringify(tokenStrings));
83 }
84 };
85 /**
86 * Returns the next token that would be returned by _readToken(), without
87 * consuming anything.
88 */
89 TokenReader.prototype.peekToken = function () {
90 return this.tokens[this._currentIndex];
91 };
92 /**
93 * Returns the TokenKind for the next token that would be returned by _readToken(), without
94 * consuming anything.
95 */
96 TokenReader.prototype.peekTokenKind = function () {
97 if (this._currentIndex >= this._readerEndIndex) {
98 return TokenKind.EndOfInput;
99 }
100 return this.tokens[this._currentIndex].kind;
101 };
102 /**
103 * Like peekTokenKind(), but looks ahead two tokens.
104 */
105 TokenReader.prototype.peekTokenAfterKind = function () {
106 if (this._currentIndex + 1 >= this._readerEndIndex) {
107 return TokenKind.EndOfInput;
108 }
109 return this.tokens[this._currentIndex + 1].kind;
110 };
111 /**
112 * Like peekTokenKind(), but looks ahead three tokens.
113 */
114 TokenReader.prototype.peekTokenAfterAfterKind = function () {
115 if (this._currentIndex + 2 >= this._readerEndIndex) {
116 return TokenKind.EndOfInput;
117 }
118 return this.tokens[this._currentIndex + 2].kind;
119 };
120 /**
121 * Extract the next token from the input stream and return it.
122 * The token will also be appended to the accumulated sequence, which can
123 * later be accessed via extractAccumulatedSequence().
124 */
125 TokenReader.prototype.readToken = function () {
126 if (this._currentIndex >= this._readerEndIndex) {
127 // If this happens, it's a parser bug
128 throw new Error('Cannot read past end of stream');
129 }
130 var token = this.tokens[this._currentIndex];
131 if (token.kind === TokenKind.EndOfInput) {
132 // We don't allow reading the EndOfInput token, because we want _peekToken()
133 // to be always guaranteed to return a valid result.
134 // If this happens, it's a parser bug
135 throw new Error('The EndOfInput token cannot be read');
136 }
137 this._currentIndex++;
138 return token;
139 };
140 /**
141 * Returns the kind of the token immediately before the current token.
142 */
143 TokenReader.prototype.peekPreviousTokenKind = function () {
144 if (this._currentIndex === 0) {
145 return TokenKind.EndOfInput;
146 }
147 return this.tokens[this._currentIndex - 1].kind;
148 };
149 /**
150 * Remembers the current position in the stream.
151 */
152 TokenReader.prototype.createMarker = function () {
153 return this._currentIndex;
154 };
155 /**
156 * Rewinds the stream pointer to a previous position in the stream.
157 */
158 TokenReader.prototype.backtrackToMarker = function (marker) {
159 if (marker > this._currentIndex) {
160 // If this happens, it's a parser bug
161 throw new Error('The marker has expired');
162 }
163 this._currentIndex = marker;
164 if (marker < this._accumulatedStartIndex) {
165 this._accumulatedStartIndex = marker;
166 }
167 };
168 return TokenReader;
169}());
170export { TokenReader };
171//# sourceMappingURL=TokenReader.js.map
\No newline at end of file