xbintsc 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +191 -0
- package/bin/xbintsc.js +26 -0
- package/dist/src/ast/factory.d.ts +11 -0
- package/dist/src/ast/factory.js +36 -0
- package/dist/src/ast/factory.js.map +1 -0
- package/dist/src/ast/nodes.d.ts +848 -0
- package/dist/src/ast/nodes.js +247 -0
- package/dist/src/ast/nodes.js.map +1 -0
- package/dist/src/ast/visitor.d.ts +18 -0
- package/dist/src/ast/visitor.js +52 -0
- package/dist/src/ast/visitor.js.map +1 -0
- package/dist/src/binder/binder.d.ts +82 -0
- package/dist/src/binder/binder.js +443 -0
- package/dist/src/binder/binder.js.map +1 -0
- package/dist/src/cli/main.d.ts +16 -0
- package/dist/src/cli/main.js +191 -0
- package/dist/src/cli/main.js.map +1 -0
- package/dist/src/codegen/llvm.d.ts +31 -0
- package/dist/src/codegen/llvm.js +1178 -0
- package/dist/src/codegen/llvm.js.map +1 -0
- package/dist/src/codegen/values.d.ts +23 -0
- package/dist/src/codegen/values.js +32 -0
- package/dist/src/codegen/values.js.map +1 -0
- package/dist/src/diagnostics/diagnostic.d.ts +69 -0
- package/dist/src/diagnostics/diagnostic.js +104 -0
- package/dist/src/diagnostics/diagnostic.js.map +1 -0
- package/dist/src/diagnostics/source.d.ts +43 -0
- package/dist/src/diagnostics/source.js +73 -0
- package/dist/src/diagnostics/source.js.map +1 -0
- package/dist/src/driver/cache.d.ts +37 -0
- package/dist/src/driver/cache.js +82 -0
- package/dist/src/driver/cache.js.map +1 -0
- package/dist/src/driver/compiler.d.ts +41 -0
- package/dist/src/driver/compiler.js +128 -0
- package/dist/src/driver/compiler.js.map +1 -0
- package/dist/src/driver/index.d.ts +4 -0
- package/dist/src/driver/index.js +5 -0
- package/dist/src/driver/index.js.map +1 -0
- package/dist/src/driver/paths.d.ts +4 -0
- package/dist/src/driver/paths.js +22 -0
- package/dist/src/driver/paths.js.map +1 -0
- package/dist/src/driver/toolchain.d.ts +43 -0
- package/dist/src/driver/toolchain.js +76 -0
- package/dist/src/driver/toolchain.js.map +1 -0
- package/dist/src/extensions/node.d.ts +9 -0
- package/dist/src/extensions/node.js +20 -0
- package/dist/src/extensions/node.js.map +1 -0
- package/dist/src/extensions/registry.d.ts +45 -0
- package/dist/src/extensions/registry.js +76 -0
- package/dist/src/extensions/registry.js.map +1 -0
- package/dist/src/index.d.ts +18 -0
- package/dist/src/index.js +19 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/lexer/scanner.d.ts +48 -0
- package/dist/src/lexer/scanner.js +650 -0
- package/dist/src/lexer/scanner.js.map +1 -0
- package/dist/src/lexer/token.d.ts +170 -0
- package/dist/src/lexer/token.js +250 -0
- package/dist/src/lexer/token.js.map +1 -0
- package/dist/src/parser/parser.d.ts +127 -0
- package/dist/src/parser/parser.js +2334 -0
- package/dist/src/parser/parser.js.map +1 -0
- package/dist/tests/binder/binder.test.d.ts +1 -0
- package/dist/tests/binder/binder.test.js +89 -0
- package/dist/tests/binder/binder.test.js.map +1 -0
- package/dist/tests/cli/main.test.d.ts +1 -0
- package/dist/tests/cli/main.test.js +68 -0
- package/dist/tests/cli/main.test.js.map +1 -0
- package/dist/tests/cli/process.test.d.ts +9 -0
- package/dist/tests/cli/process.test.js +57 -0
- package/dist/tests/cli/process.test.js.map +1 -0
- package/dist/tests/codegen/llvm.test.d.ts +1 -0
- package/dist/tests/codegen/llvm.test.js +93 -0
- package/dist/tests/codegen/llvm.test.js.map +1 -0
- package/dist/tests/driver/cache.test.d.ts +1 -0
- package/dist/tests/driver/cache.test.js +66 -0
- package/dist/tests/driver/cache.test.js.map +1 -0
- package/dist/tests/driver/compiler.test.d.ts +1 -0
- package/dist/tests/driver/compiler.test.js +80 -0
- package/dist/tests/driver/compiler.test.js.map +1 -0
- package/dist/tests/driver/toolchain.test.d.ts +1 -0
- package/dist/tests/driver/toolchain.test.js +60 -0
- package/dist/tests/driver/toolchain.test.js.map +1 -0
- package/dist/tests/e2e/compile.test.d.ts +5 -0
- package/dist/tests/e2e/compile.test.js +93 -0
- package/dist/tests/e2e/compile.test.js.map +1 -0
- package/dist/tests/extensions/registry.test.d.ts +1 -0
- package/dist/tests/extensions/registry.test.js +47 -0
- package/dist/tests/extensions/registry.test.js.map +1 -0
- package/dist/tests/helpers.d.ts +23 -0
- package/dist/tests/helpers.js +41 -0
- package/dist/tests/helpers.js.map +1 -0
- package/dist/tests/lexer/scanner.test.d.ts +1 -0
- package/dist/tests/lexer/scanner.test.js +95 -0
- package/dist/tests/lexer/scanner.test.js.map +1 -0
- package/dist/tests/parser/parser.test.d.ts +1 -0
- package/dist/tests/parser/parser.test.js +137 -0
- package/dist/tests/parser/parser.test.js.map +1 -0
- package/package.json +49 -0
- package/runtime/ext_node.c +42 -0
- package/runtime/rt.h +177 -0
- package/runtime/xt_runtime.c +682 -0
- package/scripts/build-runtime.mjs +34 -0
- package/src/ast/factory.ts +37 -0
- package/src/ast/nodes.ts +1111 -0
- package/src/ast/visitor.ts +60 -0
- package/src/binder/binder.ts +549 -0
- package/src/cli/main.ts +210 -0
- package/src/codegen/llvm.ts +1324 -0
- package/src/codegen/values.ts +36 -0
- package/src/diagnostics/diagnostic.ts +132 -0
- package/src/diagnostics/source.ts +97 -0
- package/src/driver/cache.ts +96 -0
- package/src/driver/compiler.ts +197 -0
- package/src/driver/index.ts +4 -0
- package/src/driver/paths.ts +24 -0
- package/src/driver/toolchain.ts +103 -0
- package/src/extensions/node.ts +23 -0
- package/src/extensions/registry.ts +101 -0
- package/src/index.ts +33 -0
- package/src/lexer/scanner.ts +650 -0
- package/src/lexer/token.ts +266 -0
- package/src/parser/parser.ts +2396 -0
|
@@ -0,0 +1,2334 @@
|
|
|
1
|
+
import { DiagnosticCode } from "../diagnostics/diagnostic.js";
|
|
2
|
+
import { Scanner } from "../lexer/scanner.js";
|
|
3
|
+
import { isOfKeyword, TokenKind } from "../lexer/token.js";
|
|
4
|
+
import { AssignmentOperator, BinaryOperator, ModifierKind, NodeFlags, PostfixUnaryOperator, PrefixUnaryOperator, SyntaxKind, TypeOperator, } from "../ast/nodes.js";
|
|
5
|
+
/**
|
|
6
|
+
* Recursive-descent parser for TypeScript.
|
|
7
|
+
*
|
|
8
|
+
* Type syntax is parsed for structure but erased before code generation; the
|
|
9
|
+
* parser is therefore permissive about types while the backend focuses on the
|
|
10
|
+
* value-level language.
|
|
11
|
+
*/
|
|
12
|
+
export class Parser {
|
|
13
|
+
scanner;
|
|
14
|
+
diagnostics;
|
|
15
|
+
source;
|
|
16
|
+
tokens;
|
|
17
|
+
index = 0;
|
|
18
|
+
/** Non-zero while a speculative parse is running; errors abort the attempt. */
|
|
19
|
+
speculating = 0;
|
|
20
|
+
/** Monotonic count of tokens consumed; used to detect lack of progress. */
|
|
21
|
+
consumed = 0;
|
|
22
|
+
constructor(source, diagnostics) {
|
|
23
|
+
this.source = source;
|
|
24
|
+
this.diagnostics = diagnostics;
|
|
25
|
+
this.scanner = new Scanner(source, diagnostics);
|
|
26
|
+
this.tokens = [this.scanner.nextToken()];
|
|
27
|
+
}
|
|
28
|
+
// -- token access ---------------------------------------------------------
|
|
29
|
+
get token() {
|
|
30
|
+
return this.tokens[this.index];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Compare the current token kind without triggering TypeScript's control-flow
|
|
34
|
+
* narrowing of `this.token.kind`, which would otherwise be invalidated by the
|
|
35
|
+
* calls to `nextToken()` that occur between checks.
|
|
36
|
+
*/
|
|
37
|
+
at(kind) {
|
|
38
|
+
return this.tokens[this.index].kind === kind;
|
|
39
|
+
}
|
|
40
|
+
atAhead(k, kind) {
|
|
41
|
+
return this.lookAhead(k).kind === kind;
|
|
42
|
+
}
|
|
43
|
+
lookAhead(k) {
|
|
44
|
+
while (this.tokens.length <= this.index + k) {
|
|
45
|
+
this.tokens.push(this.scanner.nextToken());
|
|
46
|
+
}
|
|
47
|
+
return this.tokens[this.index + k];
|
|
48
|
+
}
|
|
49
|
+
nextToken() {
|
|
50
|
+
this.consumed++;
|
|
51
|
+
const current = this.token;
|
|
52
|
+
if (this.index + 1 < this.tokens.length) {
|
|
53
|
+
this.index++;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
this.tokens = [this.scanner.nextToken()];
|
|
57
|
+
this.index = 0;
|
|
58
|
+
}
|
|
59
|
+
return current;
|
|
60
|
+
}
|
|
61
|
+
parseExpected(kind, message) {
|
|
62
|
+
if (this.token.kind === kind)
|
|
63
|
+
return this.nextToken();
|
|
64
|
+
this.error(DiagnosticCode.ExpectedToken, message ?? `Expected '${kind}' but found '${this.token.text || this.token.kind}'`, this.token);
|
|
65
|
+
return this.token;
|
|
66
|
+
}
|
|
67
|
+
error(code, message, token = this.token) {
|
|
68
|
+
this.diagnostics.error(code, message, { start: token.start, end: token.end }, this.source.fileName);
|
|
69
|
+
if (this.speculating > 0)
|
|
70
|
+
throw new SpeculationError();
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Run `fn` speculatively. Any diagnostic raised while it runs (which aborts
|
|
74
|
+
* the attempt via `SpeculationError`) is rolled back along with the scanner
|
|
75
|
+
* position, so the parser can try a different production cleanly.
|
|
76
|
+
*/
|
|
77
|
+
tryParse(fn) {
|
|
78
|
+
const mark = this.diagnostics.mark();
|
|
79
|
+
const state = this.saveState();
|
|
80
|
+
this.speculating++;
|
|
81
|
+
let result;
|
|
82
|
+
let ok = false;
|
|
83
|
+
try {
|
|
84
|
+
result = fn();
|
|
85
|
+
ok = true;
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
if (!(error instanceof SpeculationError))
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
this.speculating--;
|
|
93
|
+
}
|
|
94
|
+
if (!ok) {
|
|
95
|
+
this.diagnostics.reset(mark);
|
|
96
|
+
this.restoreState(state);
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
isIdentifierLike(token) {
|
|
102
|
+
if (token.kind === TokenKind.Identifier)
|
|
103
|
+
return true;
|
|
104
|
+
switch (token.kind) {
|
|
105
|
+
case TokenKind.AsKeyword:
|
|
106
|
+
case TokenKind.SatisfiesKeyword:
|
|
107
|
+
case TokenKind.FromKeyword:
|
|
108
|
+
case TokenKind.TypeKeyword:
|
|
109
|
+
case TokenKind.GetKeyword:
|
|
110
|
+
case TokenKind.SetKeyword:
|
|
111
|
+
case TokenKind.AsyncKeyword:
|
|
112
|
+
return true;
|
|
113
|
+
default:
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
parseIdentifier(allowKeywords = false) {
|
|
118
|
+
const token = this.token;
|
|
119
|
+
if (token.kind === TokenKind.Identifier || (allowKeywords && this.isIdentifierLike(token))) {
|
|
120
|
+
this.nextToken();
|
|
121
|
+
return { kind: SyntaxKind.Identifier, text: token.text, start: token.start, end: token.end };
|
|
122
|
+
}
|
|
123
|
+
this.error(DiagnosticCode.ExpectedIdentifier, `Expected identifier but found '${token.text || token.kind}'`);
|
|
124
|
+
if (token.kind !== TokenKind.EndOfFile)
|
|
125
|
+
this.nextToken();
|
|
126
|
+
return { kind: SyntaxKind.Identifier, text: token.text || "missing", start: token.start, end: token.end };
|
|
127
|
+
}
|
|
128
|
+
parseIdentifierName() {
|
|
129
|
+
return this.parseIdentifier(/* allowKeywords */ true);
|
|
130
|
+
}
|
|
131
|
+
parseSemicolon() {
|
|
132
|
+
if (this.at(TokenKind.Semicolon)) {
|
|
133
|
+
this.nextToken();
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (this.canInsertSemicolon())
|
|
137
|
+
return;
|
|
138
|
+
this.error(DiagnosticCode.ExpectedToken, "Expected ';'", this.token);
|
|
139
|
+
}
|
|
140
|
+
canInsertSemicolon() {
|
|
141
|
+
return this.at(TokenKind.EndOfFile) || this.at(TokenKind.CloseBrace) || this.token.precededByLineBreak;
|
|
142
|
+
}
|
|
143
|
+
parseDelimitedList(close, parseElement) {
|
|
144
|
+
const items = [];
|
|
145
|
+
while (this.token.kind !== close && !this.at(TokenKind.EndOfFile)) {
|
|
146
|
+
const before = this.consumed;
|
|
147
|
+
items.push(parseElement());
|
|
148
|
+
if (this.at(TokenKind.Comma))
|
|
149
|
+
this.nextToken();
|
|
150
|
+
else
|
|
151
|
+
break;
|
|
152
|
+
if (this.consumed === before && this.token.kind !== close)
|
|
153
|
+
this.nextToken();
|
|
154
|
+
}
|
|
155
|
+
this.parseExpected(close);
|
|
156
|
+
return items;
|
|
157
|
+
}
|
|
158
|
+
// -- entry point ----------------------------------------------------------
|
|
159
|
+
parseSourceFile() {
|
|
160
|
+
const statements = [];
|
|
161
|
+
while (!this.at(TokenKind.EndOfFile)) {
|
|
162
|
+
const before = this.consumed;
|
|
163
|
+
const beforePos = this.scanner.position;
|
|
164
|
+
const statement = this.parseStatement();
|
|
165
|
+
if (statement)
|
|
166
|
+
statements.push(statement);
|
|
167
|
+
if (this.consumed === before) {
|
|
168
|
+
this.error(DiagnosticCode.UnexpectedToken, `Unexpected token '${this.token.text}'`, this.token);
|
|
169
|
+
this.nextToken();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
kind: SyntaxKind.SourceFile,
|
|
174
|
+
statements,
|
|
175
|
+
fileName: this.source.fileName,
|
|
176
|
+
text: this.source.text,
|
|
177
|
+
start: 0,
|
|
178
|
+
end: this.source.text.length,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
// -- modifiers ------------------------------------------------------------
|
|
182
|
+
tryParseModifiers() {
|
|
183
|
+
const modifiers = [];
|
|
184
|
+
for (;;) {
|
|
185
|
+
const kind = this.token.kind;
|
|
186
|
+
let modifierKind;
|
|
187
|
+
switch (kind) {
|
|
188
|
+
case TokenKind.ExportKeyword:
|
|
189
|
+
modifierKind = ModifierKind.Export;
|
|
190
|
+
break;
|
|
191
|
+
case TokenKind.DefaultKeyword:
|
|
192
|
+
if (this.atAhead(1, TokenKind.FunctionKeyword) || this.atAhead(1, TokenKind.ClassKeyword)) {
|
|
193
|
+
modifierKind = ModifierKind.Default;
|
|
194
|
+
}
|
|
195
|
+
break;
|
|
196
|
+
case TokenKind.DeclareKeyword:
|
|
197
|
+
modifierKind = ModifierKind.Declare;
|
|
198
|
+
break;
|
|
199
|
+
case TokenKind.AbstractKeyword:
|
|
200
|
+
modifierKind = ModifierKind.Abstract;
|
|
201
|
+
break;
|
|
202
|
+
case TokenKind.PublicKeyword:
|
|
203
|
+
modifierKind = ModifierKind.Public;
|
|
204
|
+
break;
|
|
205
|
+
case TokenKind.PrivateKeyword:
|
|
206
|
+
modifierKind = ModifierKind.Private;
|
|
207
|
+
break;
|
|
208
|
+
case TokenKind.ProtectedKeyword:
|
|
209
|
+
modifierKind = ModifierKind.Protected;
|
|
210
|
+
break;
|
|
211
|
+
case TokenKind.StaticKeyword:
|
|
212
|
+
modifierKind = ModifierKind.Static;
|
|
213
|
+
break;
|
|
214
|
+
case TokenKind.ReadonlyKeyword:
|
|
215
|
+
modifierKind = ModifierKind.Readonly;
|
|
216
|
+
break;
|
|
217
|
+
case TokenKind.AsyncKeyword:
|
|
218
|
+
if (!this.lookAhead(1).precededByLineBreak)
|
|
219
|
+
modifierKind = ModifierKind.Async;
|
|
220
|
+
break;
|
|
221
|
+
default:
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
if (!modifierKind)
|
|
225
|
+
break;
|
|
226
|
+
const token = this.nextToken();
|
|
227
|
+
modifiers.push({ kind: SyntaxKind.Unknown, modifierKind, start: token.start, end: token.end });
|
|
228
|
+
}
|
|
229
|
+
return modifiers;
|
|
230
|
+
}
|
|
231
|
+
hasModifier(modifiers, kind) {
|
|
232
|
+
return modifiers.some((m) => m.modifierKind === kind);
|
|
233
|
+
}
|
|
234
|
+
// -- statements -----------------------------------------------------------
|
|
235
|
+
parseStatement() {
|
|
236
|
+
const token = this.token;
|
|
237
|
+
switch (token.kind) {
|
|
238
|
+
case TokenKind.OpenBrace:
|
|
239
|
+
return this.parseBlock();
|
|
240
|
+
case TokenKind.Semicolon: {
|
|
241
|
+
const t = this.nextToken();
|
|
242
|
+
return { kind: SyntaxKind.EmptyStatement, start: t.start, end: t.end };
|
|
243
|
+
}
|
|
244
|
+
case TokenKind.VarKeyword:
|
|
245
|
+
case TokenKind.LetKeyword:
|
|
246
|
+
case TokenKind.ConstKeyword:
|
|
247
|
+
return this.parseVariableStatement([]);
|
|
248
|
+
case TokenKind.FunctionKeyword:
|
|
249
|
+
return this.parseFunctionDeclaration([]);
|
|
250
|
+
case TokenKind.AsyncKeyword:
|
|
251
|
+
if (this.atAhead(1, TokenKind.FunctionKeyword) && !this.lookAhead(1).precededByLineBreak) {
|
|
252
|
+
const asyncMod = this.tryParseModifiers();
|
|
253
|
+
return this.parseFunctionDeclaration(asyncMod);
|
|
254
|
+
}
|
|
255
|
+
break;
|
|
256
|
+
case TokenKind.ClassKeyword:
|
|
257
|
+
return this.parseClassDeclaration([]);
|
|
258
|
+
case TokenKind.IfKeyword:
|
|
259
|
+
return this.parseIfStatement();
|
|
260
|
+
case TokenKind.WhileKeyword:
|
|
261
|
+
return this.parseWhileStatement();
|
|
262
|
+
case TokenKind.DoKeyword:
|
|
263
|
+
return this.parseDoStatement();
|
|
264
|
+
case TokenKind.ForKeyword:
|
|
265
|
+
return this.parseForStatement();
|
|
266
|
+
case TokenKind.ReturnKeyword:
|
|
267
|
+
return this.parseReturnStatement();
|
|
268
|
+
case TokenKind.BreakKeyword:
|
|
269
|
+
return this.parseBreakStatement();
|
|
270
|
+
case TokenKind.ContinueKeyword:
|
|
271
|
+
return this.parseContinueStatement();
|
|
272
|
+
case TokenKind.ThrowKeyword:
|
|
273
|
+
return this.parseThrowStatement();
|
|
274
|
+
case TokenKind.TryKeyword:
|
|
275
|
+
return this.parseTryStatement();
|
|
276
|
+
case TokenKind.SwitchKeyword:
|
|
277
|
+
return this.parseSwitchStatement();
|
|
278
|
+
case TokenKind.DebuggerKeyword: {
|
|
279
|
+
const t = this.nextToken();
|
|
280
|
+
this.parseSemicolon();
|
|
281
|
+
return { kind: SyntaxKind.DebuggerStatement, start: t.start, end: t.end };
|
|
282
|
+
}
|
|
283
|
+
case TokenKind.ImportKeyword:
|
|
284
|
+
if (this.atAhead(1, TokenKind.OpenParen) || this.atAhead(1, TokenKind.Dot))
|
|
285
|
+
break;
|
|
286
|
+
return this.parseImportDeclaration();
|
|
287
|
+
case TokenKind.ExportKeyword:
|
|
288
|
+
return this.parseExportDeclaration();
|
|
289
|
+
case TokenKind.InterfaceKeyword:
|
|
290
|
+
return this.parseInterfaceDeclaration([]);
|
|
291
|
+
case TokenKind.TypeKeyword:
|
|
292
|
+
if (this.isIdentifierLike(this.lookAhead(1)))
|
|
293
|
+
return this.parseTypeAliasDeclaration([]);
|
|
294
|
+
break;
|
|
295
|
+
case TokenKind.EnumKeyword:
|
|
296
|
+
return this.parseEnumDeclaration([]);
|
|
297
|
+
case TokenKind.NamespaceKeyword:
|
|
298
|
+
case TokenKind.ModuleKeyword:
|
|
299
|
+
return this.parseModuleDeclaration([]);
|
|
300
|
+
case TokenKind.DeclareKeyword: {
|
|
301
|
+
const modifiers = this.tryParseModifiers();
|
|
302
|
+
return this.parseStatementWithModifiers(modifiers);
|
|
303
|
+
}
|
|
304
|
+
default:
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
return this.parseExpressionStatement();
|
|
308
|
+
}
|
|
309
|
+
parseStatementWithModifiers(modifiers) {
|
|
310
|
+
switch (this.token.kind) {
|
|
311
|
+
case TokenKind.FunctionKeyword:
|
|
312
|
+
return this.parseFunctionDeclaration(modifiers);
|
|
313
|
+
case TokenKind.ClassKeyword:
|
|
314
|
+
return this.parseClassDeclaration(modifiers);
|
|
315
|
+
case TokenKind.InterfaceKeyword:
|
|
316
|
+
return this.parseInterfaceDeclaration(modifiers);
|
|
317
|
+
case TokenKind.TypeKeyword:
|
|
318
|
+
return this.parseTypeAliasDeclaration(modifiers);
|
|
319
|
+
case TokenKind.EnumKeyword:
|
|
320
|
+
return this.parseEnumDeclaration(modifiers);
|
|
321
|
+
case TokenKind.NamespaceKeyword:
|
|
322
|
+
case TokenKind.ModuleKeyword:
|
|
323
|
+
return this.parseModuleDeclaration(modifiers);
|
|
324
|
+
default:
|
|
325
|
+
return this.parseExpressionStatement();
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
parseBlock() {
|
|
329
|
+
const open = this.parseExpected(TokenKind.OpenBrace);
|
|
330
|
+
const statements = [];
|
|
331
|
+
while (!this.at(TokenKind.CloseBrace) && !this.at(TokenKind.EndOfFile)) {
|
|
332
|
+
const before = this.consumed;
|
|
333
|
+
const beforePos = this.scanner.position;
|
|
334
|
+
const s = this.parseStatement();
|
|
335
|
+
if (s)
|
|
336
|
+
statements.push(s);
|
|
337
|
+
if (this.consumed === before) {
|
|
338
|
+
this.error(DiagnosticCode.UnexpectedToken, `Unexpected token '${this.token.text}'`, this.token);
|
|
339
|
+
this.nextToken();
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
const close = this.parseExpected(TokenKind.CloseBrace);
|
|
343
|
+
return { kind: SyntaxKind.Block, statements, start: open.start, end: close.end };
|
|
344
|
+
}
|
|
345
|
+
parseVariableStatement(modifiers) {
|
|
346
|
+
const declList = this.parseVariableDeclarationList();
|
|
347
|
+
this.parseSemicolon();
|
|
348
|
+
return { kind: SyntaxKind.VariableStatement, declarationList: declList, modifiers, start: declList.start, end: declList.end };
|
|
349
|
+
}
|
|
350
|
+
parseVariableDeclarationList() {
|
|
351
|
+
const keyword = this.nextToken();
|
|
352
|
+
const declarationKind = keyword.kind === TokenKind.ConstKeyword ? "const" : keyword.kind === TokenKind.LetKeyword ? "let" : "var";
|
|
353
|
+
const declarations = [];
|
|
354
|
+
do {
|
|
355
|
+
declarations.push(this.parseVariableDeclaration());
|
|
356
|
+
} while (this.at(TokenKind.Comma) && this.nextToken());
|
|
357
|
+
return {
|
|
358
|
+
kind: SyntaxKind.VariableDeclarationList,
|
|
359
|
+
declarations,
|
|
360
|
+
declarationKind,
|
|
361
|
+
start: keyword.start,
|
|
362
|
+
end: declarations[declarations.length - 1]?.end ?? keyword.end,
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
parseVariableDeclaration() {
|
|
366
|
+
const name = this.parseIdentifier();
|
|
367
|
+
let exclamation = false;
|
|
368
|
+
if (this.at(TokenKind.Exclamation)) {
|
|
369
|
+
this.nextToken();
|
|
370
|
+
exclamation = true;
|
|
371
|
+
}
|
|
372
|
+
let type;
|
|
373
|
+
if (this.at(TokenKind.Colon)) {
|
|
374
|
+
this.nextToken();
|
|
375
|
+
type = this.parseType();
|
|
376
|
+
}
|
|
377
|
+
let initializer;
|
|
378
|
+
if (this.at(TokenKind.Equals)) {
|
|
379
|
+
this.nextToken();
|
|
380
|
+
initializer = this.parseAssignmentExpression();
|
|
381
|
+
}
|
|
382
|
+
return {
|
|
383
|
+
kind: SyntaxKind.VariableDeclaration,
|
|
384
|
+
name,
|
|
385
|
+
exclamation,
|
|
386
|
+
type,
|
|
387
|
+
initializer,
|
|
388
|
+
start: name.start,
|
|
389
|
+
end: initializer?.end ?? type?.end ?? name.end,
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
parseIfStatement() {
|
|
393
|
+
const start = this.parseExpected(TokenKind.IfKeyword).start;
|
|
394
|
+
this.parseExpected(TokenKind.OpenParen);
|
|
395
|
+
const condition = this.parseExpression();
|
|
396
|
+
this.parseExpected(TokenKind.CloseParen);
|
|
397
|
+
const thenStatement = this.parseStatement();
|
|
398
|
+
let elseStatement;
|
|
399
|
+
if (this.at(TokenKind.ElseKeyword)) {
|
|
400
|
+
this.nextToken();
|
|
401
|
+
elseStatement = this.parseStatement();
|
|
402
|
+
}
|
|
403
|
+
return { kind: SyntaxKind.IfStatement, condition, thenStatement, elseStatement, start, end: elseStatement?.end ?? thenStatement.end };
|
|
404
|
+
}
|
|
405
|
+
parseWhileStatement() {
|
|
406
|
+
const start = this.parseExpected(TokenKind.WhileKeyword).start;
|
|
407
|
+
this.parseExpected(TokenKind.OpenParen);
|
|
408
|
+
const condition = this.parseExpression();
|
|
409
|
+
this.parseExpected(TokenKind.CloseParen);
|
|
410
|
+
const statement = this.parseStatement();
|
|
411
|
+
return { kind: SyntaxKind.WhileStatement, condition, statement, start, end: statement.end };
|
|
412
|
+
}
|
|
413
|
+
parseDoStatement() {
|
|
414
|
+
const start = this.parseExpected(TokenKind.DoKeyword).start;
|
|
415
|
+
const statement = this.parseStatement();
|
|
416
|
+
this.parseExpected(TokenKind.WhileKeyword);
|
|
417
|
+
this.parseExpected(TokenKind.OpenParen);
|
|
418
|
+
const condition = this.parseExpression();
|
|
419
|
+
const close = this.parseExpected(TokenKind.CloseParen);
|
|
420
|
+
this.parseSemicolon();
|
|
421
|
+
return { kind: SyntaxKind.DoStatement, condition, statement, start, end: close.end };
|
|
422
|
+
}
|
|
423
|
+
parseForStatement() {
|
|
424
|
+
const start = this.parseExpected(TokenKind.ForKeyword).start;
|
|
425
|
+
let awaitModifier = false;
|
|
426
|
+
if (this.at(TokenKind.AwaitKeyword)) {
|
|
427
|
+
this.nextToken();
|
|
428
|
+
awaitModifier = true;
|
|
429
|
+
}
|
|
430
|
+
this.parseExpected(TokenKind.OpenParen);
|
|
431
|
+
let initializer;
|
|
432
|
+
if (!this.at(TokenKind.Semicolon)) {
|
|
433
|
+
if (this.at(TokenKind.VarKeyword) || this.at(TokenKind.LetKeyword) || this.at(TokenKind.ConstKeyword)) {
|
|
434
|
+
initializer = this.parseVariableDeclarationList();
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
initializer = this.parseExpression(/* noIn */ true);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
if (isOfKeyword(this.token)) {
|
|
441
|
+
this.nextToken();
|
|
442
|
+
const expression = this.parseAssignmentExpression();
|
|
443
|
+
this.parseExpected(TokenKind.CloseParen);
|
|
444
|
+
const statement = this.parseStatement();
|
|
445
|
+
return { kind: SyntaxKind.ForOfStatement, initializer: initializer, expression, statement, awaitModifier, start, end: statement.end };
|
|
446
|
+
}
|
|
447
|
+
if (this.at(TokenKind.InKeyword)) {
|
|
448
|
+
this.nextToken();
|
|
449
|
+
const expression = this.parseExpression();
|
|
450
|
+
this.parseExpected(TokenKind.CloseParen);
|
|
451
|
+
const statement = this.parseStatement();
|
|
452
|
+
return { kind: SyntaxKind.ForInStatement, initializer: initializer, expression, statement, start, end: statement.end };
|
|
453
|
+
}
|
|
454
|
+
this.parseExpected(TokenKind.Semicolon);
|
|
455
|
+
let condition;
|
|
456
|
+
if (!this.at(TokenKind.Semicolon))
|
|
457
|
+
condition = this.parseExpression();
|
|
458
|
+
this.parseExpected(TokenKind.Semicolon);
|
|
459
|
+
let incrementor;
|
|
460
|
+
if (!this.at(TokenKind.CloseParen))
|
|
461
|
+
incrementor = this.parseExpression();
|
|
462
|
+
this.parseExpected(TokenKind.CloseParen);
|
|
463
|
+
const statement = this.parseStatement();
|
|
464
|
+
return { kind: SyntaxKind.ForStatement, initializer, condition, incrementor, statement, start, end: statement.end };
|
|
465
|
+
}
|
|
466
|
+
parseReturnStatement() {
|
|
467
|
+
const start = this.parseExpected(TokenKind.ReturnKeyword).start;
|
|
468
|
+
let expression;
|
|
469
|
+
if (!this.at(TokenKind.Semicolon) && !this.canInsertSemicolon())
|
|
470
|
+
expression = this.parseExpression();
|
|
471
|
+
this.parseSemicolon();
|
|
472
|
+
return { kind: SyntaxKind.ReturnStatement, expression, start, end: expression?.end ?? start + 6 };
|
|
473
|
+
}
|
|
474
|
+
parseBreakStatement() {
|
|
475
|
+
const start = this.parseExpected(TokenKind.BreakKeyword).start;
|
|
476
|
+
let label;
|
|
477
|
+
if (!this.canInsertSemicolon() && !this.at(TokenKind.Semicolon))
|
|
478
|
+
label = this.parseIdentifier();
|
|
479
|
+
this.parseSemicolon();
|
|
480
|
+
return { kind: SyntaxKind.BreakStatement, label, start, end: label?.end ?? start + 5 };
|
|
481
|
+
}
|
|
482
|
+
parseContinueStatement() {
|
|
483
|
+
const start = this.parseExpected(TokenKind.ContinueKeyword).start;
|
|
484
|
+
let label;
|
|
485
|
+
if (!this.canInsertSemicolon() && !this.at(TokenKind.Semicolon))
|
|
486
|
+
label = this.parseIdentifier();
|
|
487
|
+
this.parseSemicolon();
|
|
488
|
+
return { kind: SyntaxKind.ContinueStatement, label, start, end: label?.end ?? start + 8 };
|
|
489
|
+
}
|
|
490
|
+
parseThrowStatement() {
|
|
491
|
+
const start = this.parseExpected(TokenKind.ThrowKeyword).start;
|
|
492
|
+
const expression = this.parseExpression();
|
|
493
|
+
this.parseSemicolon();
|
|
494
|
+
return { kind: SyntaxKind.ThrowStatement, expression, start, end: expression.end };
|
|
495
|
+
}
|
|
496
|
+
parseTryStatement() {
|
|
497
|
+
const start = this.parseExpected(TokenKind.TryKeyword).start;
|
|
498
|
+
const tryBlock = this.parseBlock();
|
|
499
|
+
let catchClause;
|
|
500
|
+
if (this.at(TokenKind.CatchKeyword)) {
|
|
501
|
+
const catchStart = this.nextToken().start;
|
|
502
|
+
let variable;
|
|
503
|
+
let type;
|
|
504
|
+
if (this.at(TokenKind.OpenParen)) {
|
|
505
|
+
this.nextToken();
|
|
506
|
+
variable = this.parseIdentifier();
|
|
507
|
+
if (this.at(TokenKind.Colon)) {
|
|
508
|
+
this.nextToken();
|
|
509
|
+
type = this.parseType();
|
|
510
|
+
}
|
|
511
|
+
this.parseExpected(TokenKind.CloseParen);
|
|
512
|
+
}
|
|
513
|
+
const block = this.parseBlock();
|
|
514
|
+
catchClause = { kind: SyntaxKind.CatchClause, variable, type, block, start: catchStart, end: block.end };
|
|
515
|
+
}
|
|
516
|
+
let finallyBlock;
|
|
517
|
+
if (this.at(TokenKind.FinallyKeyword)) {
|
|
518
|
+
this.nextToken();
|
|
519
|
+
finallyBlock = this.parseBlock();
|
|
520
|
+
}
|
|
521
|
+
return { kind: SyntaxKind.TryStatement, tryBlock, catchClause, finallyBlock, start, end: finallyBlock?.end ?? catchClause?.end ?? tryBlock.end };
|
|
522
|
+
}
|
|
523
|
+
parseSwitchStatement() {
|
|
524
|
+
const start = this.parseExpected(TokenKind.SwitchKeyword).start;
|
|
525
|
+
this.parseExpected(TokenKind.OpenParen);
|
|
526
|
+
const expression = this.parseExpression();
|
|
527
|
+
this.parseExpected(TokenKind.CloseParen);
|
|
528
|
+
this.parseExpected(TokenKind.OpenBrace);
|
|
529
|
+
const clauses = [];
|
|
530
|
+
while (!this.at(TokenKind.CloseBrace) && !this.at(TokenKind.EndOfFile)) {
|
|
531
|
+
if (this.at(TokenKind.CaseKeyword)) {
|
|
532
|
+
const caseStart = this.nextToken().start;
|
|
533
|
+
const caseExpression = this.parseExpression();
|
|
534
|
+
this.parseExpected(TokenKind.Colon);
|
|
535
|
+
const statements = [];
|
|
536
|
+
while (!this.at(TokenKind.CaseKeyword) &&
|
|
537
|
+
!this.at(TokenKind.DefaultKeyword) &&
|
|
538
|
+
!this.at(TokenKind.CloseBrace) &&
|
|
539
|
+
!this.at(TokenKind.EndOfFile)) {
|
|
540
|
+
const s = this.parseStatement();
|
|
541
|
+
if (s)
|
|
542
|
+
statements.push(s);
|
|
543
|
+
}
|
|
544
|
+
clauses.push({ kind: SyntaxKind.CaseClause, expression: caseExpression, statements, start: caseStart, end: statements[statements.length - 1]?.end ?? caseExpression.end });
|
|
545
|
+
}
|
|
546
|
+
else if (this.at(TokenKind.DefaultKeyword)) {
|
|
547
|
+
const defaultStart = this.nextToken().start;
|
|
548
|
+
this.parseExpected(TokenKind.Colon);
|
|
549
|
+
const statements = [];
|
|
550
|
+
while (!this.at(TokenKind.CaseKeyword) && !this.at(TokenKind.CloseBrace) && !this.at(TokenKind.EndOfFile)) {
|
|
551
|
+
const s = this.parseStatement();
|
|
552
|
+
if (s)
|
|
553
|
+
statements.push(s);
|
|
554
|
+
}
|
|
555
|
+
clauses.push({ kind: SyntaxKind.DefaultClause, statements, start: defaultStart, end: statements[statements.length - 1]?.end ?? defaultStart });
|
|
556
|
+
}
|
|
557
|
+
else {
|
|
558
|
+
this.error(DiagnosticCode.UnexpectedToken, `Unexpected token '${this.token.text}' in switch body`);
|
|
559
|
+
this.nextToken();
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
const close = this.parseExpected(TokenKind.CloseBrace);
|
|
563
|
+
return { kind: SyntaxKind.SwitchStatement, expression, clauses, start, end: close.end };
|
|
564
|
+
}
|
|
565
|
+
parseExpressionStatement() {
|
|
566
|
+
const expression = this.parseExpression();
|
|
567
|
+
this.parseSemicolon();
|
|
568
|
+
return { kind: SyntaxKind.ExpressionStatement, expression, start: expression.start, end: expression.end };
|
|
569
|
+
}
|
|
570
|
+
// -- declarations ---------------------------------------------------------
|
|
571
|
+
parseFunctionDeclaration(modifiers) {
|
|
572
|
+
const start = this.parseExpected(TokenKind.FunctionKeyword).start;
|
|
573
|
+
let flags = NodeFlags.None;
|
|
574
|
+
if (this.at(TokenKind.Asterisk)) {
|
|
575
|
+
this.nextToken();
|
|
576
|
+
flags |= NodeFlags.Generator;
|
|
577
|
+
}
|
|
578
|
+
if (this.hasModifier(modifiers, ModifierKind.Async))
|
|
579
|
+
flags |= NodeFlags.Async;
|
|
580
|
+
if (this.hasModifier(modifiers, ModifierKind.Declare))
|
|
581
|
+
flags |= NodeFlags.Declare;
|
|
582
|
+
let name;
|
|
583
|
+
if (!this.at(TokenKind.OpenParen) && !this.at(TokenKind.LessThan))
|
|
584
|
+
name = this.parseIdentifier();
|
|
585
|
+
const typeParameters = this.parseTypeParameters();
|
|
586
|
+
const parameters = this.parseParameters();
|
|
587
|
+
let returnType;
|
|
588
|
+
if (this.at(TokenKind.Colon)) {
|
|
589
|
+
this.nextToken();
|
|
590
|
+
returnType = this.parseReturnType();
|
|
591
|
+
}
|
|
592
|
+
let body;
|
|
593
|
+
if (this.at(TokenKind.OpenBrace))
|
|
594
|
+
body = this.parseBlock();
|
|
595
|
+
else
|
|
596
|
+
this.parseSemicolon();
|
|
597
|
+
return {
|
|
598
|
+
kind: SyntaxKind.FunctionDeclaration,
|
|
599
|
+
name,
|
|
600
|
+
modifiers,
|
|
601
|
+
typeParameters,
|
|
602
|
+
parameters,
|
|
603
|
+
returnType,
|
|
604
|
+
body,
|
|
605
|
+
flags,
|
|
606
|
+
start,
|
|
607
|
+
end: body?.end ?? returnType?.end ?? parameters[parameters.length - 1]?.end ?? start,
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
parseTypeParameters() {
|
|
611
|
+
if (!this.at(TokenKind.LessThan))
|
|
612
|
+
return [];
|
|
613
|
+
this.nextToken();
|
|
614
|
+
const params = [];
|
|
615
|
+
while (!this.at(TokenKind.GreaterThan) && !this.at(TokenKind.EndOfFile)) {
|
|
616
|
+
const name = this.parseIdentifier();
|
|
617
|
+
let constraint;
|
|
618
|
+
if (this.at(TokenKind.ExtendsKeyword)) {
|
|
619
|
+
this.nextToken();
|
|
620
|
+
constraint = this.parseType();
|
|
621
|
+
}
|
|
622
|
+
let defaultType;
|
|
623
|
+
if (this.at(TokenKind.Equals)) {
|
|
624
|
+
this.nextToken();
|
|
625
|
+
defaultType = this.parseType();
|
|
626
|
+
}
|
|
627
|
+
params.push({ kind: SyntaxKind.TypeParameterDeclaration, name, constraint, default: defaultType, start: name.start, end: defaultType?.end ?? constraint?.end ?? name.end });
|
|
628
|
+
if (this.at(TokenKind.Comma))
|
|
629
|
+
this.nextToken();
|
|
630
|
+
else
|
|
631
|
+
break;
|
|
632
|
+
}
|
|
633
|
+
this.parseGreaterThan();
|
|
634
|
+
return params;
|
|
635
|
+
}
|
|
636
|
+
/** Consume a `>` that may have been lexed as `>>` / `>>>`, splitting it. */
|
|
637
|
+
parseGreaterThan() {
|
|
638
|
+
const token = this.token;
|
|
639
|
+
if (token.kind === TokenKind.GreaterThan) {
|
|
640
|
+
this.nextToken();
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
if (token.kind === TokenKind.GreaterThanGreaterThan || token.kind === TokenKind.GreaterThanGreaterThanGreaterThan) {
|
|
644
|
+
const extra = token.kind === TokenKind.GreaterThanGreaterThan ? 1 : 2;
|
|
645
|
+
this.tokens[this.index] = {
|
|
646
|
+
kind: extra === 1 ? TokenKind.GreaterThan : TokenKind.GreaterThanGreaterThan,
|
|
647
|
+
start: token.start + 1,
|
|
648
|
+
end: token.end,
|
|
649
|
+
text: token.text.slice(1),
|
|
650
|
+
precededByLineBreak: false,
|
|
651
|
+
};
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
this.parseExpected(TokenKind.GreaterThan);
|
|
655
|
+
}
|
|
656
|
+
parseParameters() {
|
|
657
|
+
this.parseExpected(TokenKind.OpenParen);
|
|
658
|
+
const params = [];
|
|
659
|
+
while (!this.at(TokenKind.CloseParen) && !this.at(TokenKind.EndOfFile)) {
|
|
660
|
+
const start = this.token.start;
|
|
661
|
+
const modifiers = this.tryParseModifiers();
|
|
662
|
+
let dotDotDotToken = false;
|
|
663
|
+
if (this.at(TokenKind.DotDotDot)) {
|
|
664
|
+
this.nextToken();
|
|
665
|
+
dotDotDotToken = true;
|
|
666
|
+
}
|
|
667
|
+
const name = this.parseIdentifier();
|
|
668
|
+
let questionToken = false;
|
|
669
|
+
if (this.at(TokenKind.Question)) {
|
|
670
|
+
this.nextToken();
|
|
671
|
+
questionToken = true;
|
|
672
|
+
}
|
|
673
|
+
let type;
|
|
674
|
+
if (this.at(TokenKind.Colon)) {
|
|
675
|
+
this.nextToken();
|
|
676
|
+
type = this.parseType();
|
|
677
|
+
}
|
|
678
|
+
let initializer;
|
|
679
|
+
if (this.at(TokenKind.Equals)) {
|
|
680
|
+
this.nextToken();
|
|
681
|
+
initializer = this.parseAssignmentExpression();
|
|
682
|
+
}
|
|
683
|
+
params.push({ kind: SyntaxKind.Parameter, name, modifiers, dotDotDotToken, questionToken, type, initializer, start, end: initializer?.end ?? type?.end ?? name.end });
|
|
684
|
+
if (this.at(TokenKind.Comma))
|
|
685
|
+
this.nextToken();
|
|
686
|
+
else
|
|
687
|
+
break;
|
|
688
|
+
}
|
|
689
|
+
this.parseExpected(TokenKind.CloseParen);
|
|
690
|
+
return params;
|
|
691
|
+
}
|
|
692
|
+
parseReturnType() {
|
|
693
|
+
if (this.at(TokenKind.AssertsKeyword)) {
|
|
694
|
+
const start = this.nextToken().start;
|
|
695
|
+
let paramName;
|
|
696
|
+
if (this.isIdentifierLike(this.token) || this.at(TokenKind.ThisKeyword))
|
|
697
|
+
paramName = this.parseIdentifierName();
|
|
698
|
+
let predicateType;
|
|
699
|
+
if (this.at(TokenKind.IsKeyword)) {
|
|
700
|
+
this.nextToken();
|
|
701
|
+
predicateType = this.parseType();
|
|
702
|
+
}
|
|
703
|
+
const stub = { kind: SyntaxKind.Identifier, text: paramName?.text ?? "this", start: paramName?.start ?? start, end: paramName?.end ?? start };
|
|
704
|
+
return { kind: SyntaxKind.TypePredicate, parameterName: stub, type: predicateType, start, end: predicateType?.end ?? start };
|
|
705
|
+
}
|
|
706
|
+
if (this.isIdentifierLike(this.token) && this.atAhead(1, TokenKind.IsKeyword)) {
|
|
707
|
+
const paramName = this.parseIdentifierName();
|
|
708
|
+
this.nextToken();
|
|
709
|
+
const predicateType = this.parseType();
|
|
710
|
+
return { kind: SyntaxKind.TypePredicate, parameterName: paramName, type: predicateType, start: paramName.start, end: predicateType.end };
|
|
711
|
+
}
|
|
712
|
+
return this.parseType();
|
|
713
|
+
}
|
|
714
|
+
parseClassDeclaration(modifiers) {
|
|
715
|
+
const start = this.parseExpected(TokenKind.ClassKeyword).start;
|
|
716
|
+
let name;
|
|
717
|
+
if (!this.at(TokenKind.OpenBrace) && !this.at(TokenKind.ExtendsKeyword) && !this.at(TokenKind.ImplementsKeyword)) {
|
|
718
|
+
name = this.parseIdentifier();
|
|
719
|
+
}
|
|
720
|
+
const typeParameters = this.parseTypeParameters();
|
|
721
|
+
const heritage = this.parseHeritageClauses();
|
|
722
|
+
const members = this.parseClassMembers();
|
|
723
|
+
return { kind: SyntaxKind.ClassDeclaration, name, modifiers, typeParameters, heritage, members, start, end: members[members.length - 1]?.end ?? start };
|
|
724
|
+
}
|
|
725
|
+
parseHeritageClauses() {
|
|
726
|
+
const clauses = [];
|
|
727
|
+
while (this.at(TokenKind.ExtendsKeyword) || this.at(TokenKind.ImplementsKeyword)) {
|
|
728
|
+
const token = this.nextToken();
|
|
729
|
+
const tokenKind = token.kind === TokenKind.ExtendsKeyword ? "extends" : "implements";
|
|
730
|
+
const types = [];
|
|
731
|
+
do {
|
|
732
|
+
const expression = this.parseLeftHandSideExpression();
|
|
733
|
+
const typeArguments = this.at(TokenKind.LessThan) ? this.parseTypeArguments() : [];
|
|
734
|
+
types.push({ kind: SyntaxKind.ExpressionWithTypeArguments, expression, typeArguments, start: expression.start, end: typeArguments[typeArguments.length - 1]?.end ?? expression.end });
|
|
735
|
+
} while (this.at(TokenKind.Comma) && this.nextToken());
|
|
736
|
+
clauses.push({ kind: SyntaxKind.HeritageClause, token: tokenKind, types, start: token.start, end: types[types.length - 1]?.end ?? token.end });
|
|
737
|
+
}
|
|
738
|
+
return clauses;
|
|
739
|
+
}
|
|
740
|
+
parseClassMembers() {
|
|
741
|
+
this.parseExpected(TokenKind.OpenBrace);
|
|
742
|
+
const members = [];
|
|
743
|
+
while (!this.at(TokenKind.CloseBrace) && !this.at(TokenKind.EndOfFile)) {
|
|
744
|
+
const before = this.consumed;
|
|
745
|
+
const member = this.parseClassElement();
|
|
746
|
+
if (member)
|
|
747
|
+
members.push(member);
|
|
748
|
+
if (this.consumed === before)
|
|
749
|
+
this.nextToken();
|
|
750
|
+
}
|
|
751
|
+
this.parseExpected(TokenKind.CloseBrace);
|
|
752
|
+
return members;
|
|
753
|
+
}
|
|
754
|
+
parseClassElement() {
|
|
755
|
+
const start = this.token.start;
|
|
756
|
+
const modifiers = this.tryParseModifiers();
|
|
757
|
+
if (this.at(TokenKind.Semicolon)) {
|
|
758
|
+
this.nextToken();
|
|
759
|
+
return undefined;
|
|
760
|
+
}
|
|
761
|
+
if (this.at(TokenKind.ConstructorKeyword)) {
|
|
762
|
+
const name = this.nextToken();
|
|
763
|
+
const parameters = this.parseParameters();
|
|
764
|
+
let body;
|
|
765
|
+
if (this.at(TokenKind.OpenBrace))
|
|
766
|
+
body = this.parseBlock();
|
|
767
|
+
else
|
|
768
|
+
this.parseSemicolon();
|
|
769
|
+
const ctor = { kind: SyntaxKind.ConstructorDeclaration, modifiers, parameters, body, start, end: body?.end ?? name.end };
|
|
770
|
+
return ctor;
|
|
771
|
+
}
|
|
772
|
+
let flags = NodeFlags.None;
|
|
773
|
+
if (this.at(TokenKind.Asterisk)) {
|
|
774
|
+
this.nextToken();
|
|
775
|
+
flags |= NodeFlags.Generator;
|
|
776
|
+
}
|
|
777
|
+
if (this.hasModifier(modifiers, ModifierKind.Async))
|
|
778
|
+
flags |= NodeFlags.Async;
|
|
779
|
+
const name = this.parsePropertyName();
|
|
780
|
+
let questionToken = false;
|
|
781
|
+
let exclamationToken = false;
|
|
782
|
+
if (this.at(TokenKind.Question)) {
|
|
783
|
+
this.nextToken();
|
|
784
|
+
questionToken = true;
|
|
785
|
+
}
|
|
786
|
+
if (this.at(TokenKind.Exclamation)) {
|
|
787
|
+
this.nextToken();
|
|
788
|
+
exclamationToken = true;
|
|
789
|
+
}
|
|
790
|
+
const typeParameters = this.at(TokenKind.LessThan) ? this.parseTypeParameters() : [];
|
|
791
|
+
if (this.at(TokenKind.OpenParen)) {
|
|
792
|
+
const parameters = this.parseParameters();
|
|
793
|
+
let returnType;
|
|
794
|
+
if (this.at(TokenKind.Colon)) {
|
|
795
|
+
this.nextToken();
|
|
796
|
+
returnType = this.parseReturnType();
|
|
797
|
+
}
|
|
798
|
+
let body;
|
|
799
|
+
if (this.at(TokenKind.OpenBrace))
|
|
800
|
+
body = this.parseBlock();
|
|
801
|
+
else
|
|
802
|
+
this.parseSemicolon();
|
|
803
|
+
const method = {
|
|
804
|
+
kind: SyntaxKind.MethodDeclaration,
|
|
805
|
+
name,
|
|
806
|
+
modifiers,
|
|
807
|
+
typeParameters,
|
|
808
|
+
parameters,
|
|
809
|
+
returnType,
|
|
810
|
+
body,
|
|
811
|
+
flags,
|
|
812
|
+
optional: questionToken,
|
|
813
|
+
start,
|
|
814
|
+
end: body?.end ?? returnType?.end ?? name.end,
|
|
815
|
+
};
|
|
816
|
+
return method;
|
|
817
|
+
}
|
|
818
|
+
let type;
|
|
819
|
+
if (this.at(TokenKind.Colon)) {
|
|
820
|
+
this.nextToken();
|
|
821
|
+
type = this.parseType();
|
|
822
|
+
}
|
|
823
|
+
let initializer;
|
|
824
|
+
if (this.at(TokenKind.Equals)) {
|
|
825
|
+
this.nextToken();
|
|
826
|
+
initializer = this.parseAssignmentExpression();
|
|
827
|
+
}
|
|
828
|
+
this.parseSemicolon();
|
|
829
|
+
const property = {
|
|
830
|
+
kind: SyntaxKind.PropertyDeclaration,
|
|
831
|
+
name,
|
|
832
|
+
modifiers,
|
|
833
|
+
questionToken,
|
|
834
|
+
exclamationToken,
|
|
835
|
+
type,
|
|
836
|
+
initializer,
|
|
837
|
+
start,
|
|
838
|
+
end: initializer?.end ?? type?.end ?? name.end,
|
|
839
|
+
};
|
|
840
|
+
return property;
|
|
841
|
+
}
|
|
842
|
+
parsePropertyName() {
|
|
843
|
+
const token = this.token;
|
|
844
|
+
if (token.kind === TokenKind.StringLiteral) {
|
|
845
|
+
this.nextToken();
|
|
846
|
+
return { kind: SyntaxKind.StringLiteral, text: token.text, raw: token.text, value: String(token.value ?? ""), start: token.start, end: token.end };
|
|
847
|
+
}
|
|
848
|
+
if (token.kind === TokenKind.NumericLiteral) {
|
|
849
|
+
this.nextToken();
|
|
850
|
+
return { kind: SyntaxKind.NumericLiteral, text: token.text, value: Number(token.value ?? 0), start: token.start, end: token.end };
|
|
851
|
+
}
|
|
852
|
+
if (token.kind === TokenKind.OpenBracket) {
|
|
853
|
+
this.nextToken();
|
|
854
|
+
const expr = this.parseAssignmentExpression();
|
|
855
|
+
this.parseExpected(TokenKind.CloseBracket);
|
|
856
|
+
const name = expressionToPropertyName(expr);
|
|
857
|
+
if (name)
|
|
858
|
+
return name;
|
|
859
|
+
this.error(DiagnosticCode.InvalidTypeSyntax, "Computed property names are not supported by xbintsc");
|
|
860
|
+
return { kind: SyntaxKind.Identifier, text: "<computed>", start: expr.start, end: expr.end };
|
|
861
|
+
}
|
|
862
|
+
return this.parseIdentifierName();
|
|
863
|
+
}
|
|
864
|
+
parseInterfaceDeclaration(modifiers) {
|
|
865
|
+
const start = this.parseExpected(TokenKind.InterfaceKeyword).start;
|
|
866
|
+
const name = this.parseIdentifier();
|
|
867
|
+
const typeParameters = this.parseTypeParameters();
|
|
868
|
+
const heritage = this.parseHeritageClauses();
|
|
869
|
+
const members = this.parseTypeMembers();
|
|
870
|
+
return { kind: SyntaxKind.InterfaceDeclaration, name, modifiers, typeParameters, heritage, members, start, end: members[members.length - 1]?.end ?? name.end };
|
|
871
|
+
}
|
|
872
|
+
parseTypeAliasDeclaration(modifiers) {
|
|
873
|
+
const start = this.parseExpected(TokenKind.TypeKeyword).start;
|
|
874
|
+
const name = this.parseIdentifier();
|
|
875
|
+
const typeParameters = this.parseTypeParameters();
|
|
876
|
+
this.parseExpected(TokenKind.Equals);
|
|
877
|
+
const type = this.parseType();
|
|
878
|
+
this.parseSemicolon();
|
|
879
|
+
return { kind: SyntaxKind.TypeAliasDeclaration, name, modifiers, typeParameters, type, start, end: type.end };
|
|
880
|
+
}
|
|
881
|
+
parseEnumDeclaration(modifiers) {
|
|
882
|
+
const start = this.parseExpected(TokenKind.EnumKeyword).start;
|
|
883
|
+
const name = this.parseIdentifier();
|
|
884
|
+
this.parseExpected(TokenKind.OpenBrace);
|
|
885
|
+
const members = [];
|
|
886
|
+
while (!this.at(TokenKind.CloseBrace) && !this.at(TokenKind.EndOfFile)) {
|
|
887
|
+
const memberName = this.parsePropertyName();
|
|
888
|
+
let initializer;
|
|
889
|
+
if (this.at(TokenKind.Equals)) {
|
|
890
|
+
this.nextToken();
|
|
891
|
+
initializer = this.parseAssignmentExpression();
|
|
892
|
+
}
|
|
893
|
+
members.push({ kind: SyntaxKind.EnumMember, name: memberName, initializer, start: memberName.start, end: initializer?.end ?? memberName.end });
|
|
894
|
+
if (this.at(TokenKind.Comma))
|
|
895
|
+
this.nextToken();
|
|
896
|
+
else
|
|
897
|
+
break;
|
|
898
|
+
}
|
|
899
|
+
const close = this.parseExpected(TokenKind.CloseBrace);
|
|
900
|
+
return { kind: SyntaxKind.EnumDeclaration, name, modifiers, members, start, end: close.end };
|
|
901
|
+
}
|
|
902
|
+
parseModuleDeclaration(modifiers) {
|
|
903
|
+
const start = this.nextToken().start;
|
|
904
|
+
let name;
|
|
905
|
+
if (this.at(TokenKind.StringLiteral)) {
|
|
906
|
+
const token = this.nextToken();
|
|
907
|
+
name = { kind: SyntaxKind.StringLiteral, text: token.text, raw: token.text, value: String(token.value ?? ""), start: token.start, end: token.end };
|
|
908
|
+
}
|
|
909
|
+
else {
|
|
910
|
+
name = this.parseIdentifierName();
|
|
911
|
+
}
|
|
912
|
+
while (this.at(TokenKind.Dot)) {
|
|
913
|
+
this.nextToken();
|
|
914
|
+
this.parseIdentifierName();
|
|
915
|
+
}
|
|
916
|
+
let body;
|
|
917
|
+
if (this.at(TokenKind.OpenBrace))
|
|
918
|
+
body = this.parseBlock();
|
|
919
|
+
else
|
|
920
|
+
this.parseSemicolon();
|
|
921
|
+
return { kind: SyntaxKind.ModuleDeclaration, name, modifiers, body, start, end: body?.end ?? name.end };
|
|
922
|
+
}
|
|
923
|
+
parseTypeMembers() {
|
|
924
|
+
this.parseExpected(TokenKind.OpenBrace);
|
|
925
|
+
const members = [];
|
|
926
|
+
while (!this.at(TokenKind.CloseBrace) && !this.at(TokenKind.EndOfFile)) {
|
|
927
|
+
const before = this.consumed;
|
|
928
|
+
const member = this.parseTypeMember();
|
|
929
|
+
if (member)
|
|
930
|
+
members.push(member);
|
|
931
|
+
if (this.consumed === before)
|
|
932
|
+
this.nextToken();
|
|
933
|
+
}
|
|
934
|
+
this.parseExpected(TokenKind.CloseBrace);
|
|
935
|
+
return members;
|
|
936
|
+
}
|
|
937
|
+
parseTypeMember() {
|
|
938
|
+
const start = this.token.start;
|
|
939
|
+
const readonlyToken = this.at(TokenKind.ReadonlyKeyword);
|
|
940
|
+
if (readonlyToken)
|
|
941
|
+
this.nextToken();
|
|
942
|
+
if (this.at(TokenKind.OpenParen) || this.at(TokenKind.LessThan)) {
|
|
943
|
+
const typeParameters = this.parseTypeParameters();
|
|
944
|
+
const parameters = this.parseParameters();
|
|
945
|
+
let returnType;
|
|
946
|
+
if (this.at(TokenKind.Colon)) {
|
|
947
|
+
this.nextToken();
|
|
948
|
+
returnType = this.parseType();
|
|
949
|
+
}
|
|
950
|
+
this.parseSemicolonOrComma();
|
|
951
|
+
const name = { kind: SyntaxKind.Identifier, text: "__call", start, end: start };
|
|
952
|
+
return { kind: SyntaxKind.MethodSignature, name, questionToken: false, typeParameters, parameters, returnType, start, end: returnType?.end ?? start };
|
|
953
|
+
}
|
|
954
|
+
if (this.at(TokenKind.OpenBracket) && this.atAhead(1, TokenKind.Identifier)) {
|
|
955
|
+
const saveIndex = this.index;
|
|
956
|
+
const savePos = this.scanner.position;
|
|
957
|
+
const savedTokens = this.tokens.slice();
|
|
958
|
+
this.nextToken();
|
|
959
|
+
const parameters = [];
|
|
960
|
+
let ok = true;
|
|
961
|
+
try {
|
|
962
|
+
const paramName = this.parseIdentifier();
|
|
963
|
+
let type;
|
|
964
|
+
if (this.at(TokenKind.Colon)) {
|
|
965
|
+
this.nextToken();
|
|
966
|
+
type = this.parseType();
|
|
967
|
+
}
|
|
968
|
+
else
|
|
969
|
+
ok = false;
|
|
970
|
+
this.parseExpected(TokenKind.CloseBracket);
|
|
971
|
+
parameters.push({ kind: SyntaxKind.Parameter, name: paramName, modifiers: [], dotDotDotToken: false, questionToken: false, type, start: paramName.start, end: type?.end ?? paramName.end });
|
|
972
|
+
}
|
|
973
|
+
catch {
|
|
974
|
+
ok = false;
|
|
975
|
+
}
|
|
976
|
+
if (ok && this.at(TokenKind.Colon)) {
|
|
977
|
+
this.nextToken();
|
|
978
|
+
const type = this.parseType();
|
|
979
|
+
this.parseSemicolonOrComma();
|
|
980
|
+
return { kind: SyntaxKind.IndexSignature, parameters, type, start, end: type.end };
|
|
981
|
+
}
|
|
982
|
+
this.index = saveIndex;
|
|
983
|
+
this.tokens = savedTokens;
|
|
984
|
+
this.scanner.seek(savePos);
|
|
985
|
+
}
|
|
986
|
+
const name = this.parsePropertyName();
|
|
987
|
+
let questionToken = false;
|
|
988
|
+
if (this.at(TokenKind.Question)) {
|
|
989
|
+
this.nextToken();
|
|
990
|
+
questionToken = true;
|
|
991
|
+
}
|
|
992
|
+
const typeParameters = this.at(TokenKind.LessThan) ? this.parseTypeParameters() : [];
|
|
993
|
+
if (this.at(TokenKind.OpenParen) || typeParameters.length > 0) {
|
|
994
|
+
const parameters = this.parseParameters();
|
|
995
|
+
let returnType;
|
|
996
|
+
if (this.at(TokenKind.Colon)) {
|
|
997
|
+
this.nextToken();
|
|
998
|
+
returnType = this.parseType();
|
|
999
|
+
}
|
|
1000
|
+
this.parseSemicolonOrComma();
|
|
1001
|
+
const sig = { kind: SyntaxKind.MethodSignature, name, questionToken, typeParameters, parameters, returnType, start, end: returnType?.end ?? name.end };
|
|
1002
|
+
return sig;
|
|
1003
|
+
}
|
|
1004
|
+
let type;
|
|
1005
|
+
if (this.at(TokenKind.Colon)) {
|
|
1006
|
+
this.nextToken();
|
|
1007
|
+
type = this.parseType();
|
|
1008
|
+
}
|
|
1009
|
+
this.parseSemicolonOrComma();
|
|
1010
|
+
const signature = { kind: SyntaxKind.PropertySignature, name, questionToken, readonlyToken, type, start, end: type?.end ?? name.end };
|
|
1011
|
+
return signature;
|
|
1012
|
+
}
|
|
1013
|
+
parseSemicolonOrComma() {
|
|
1014
|
+
if (this.at(TokenKind.Semicolon) || this.at(TokenKind.Comma))
|
|
1015
|
+
this.nextToken();
|
|
1016
|
+
}
|
|
1017
|
+
// -- modules --------------------------------------------------------------
|
|
1018
|
+
parseImportDeclaration() {
|
|
1019
|
+
const start = this.parseExpected(TokenKind.ImportKeyword).start;
|
|
1020
|
+
let importClause;
|
|
1021
|
+
if (!this.at(TokenKind.StringLiteral)) {
|
|
1022
|
+
const clauseStart = this.token.start;
|
|
1023
|
+
let isTypeOnly = false;
|
|
1024
|
+
if (this.at(TokenKind.TypeKeyword) && !this.lookAhead(1).precededByLineBreak) {
|
|
1025
|
+
const next = this.lookAhead(1);
|
|
1026
|
+
if (this.isIdentifierLike(next) || next.kind === TokenKind.OpenBrace || next.kind === TokenKind.Asterisk) {
|
|
1027
|
+
this.nextToken();
|
|
1028
|
+
isTypeOnly = true;
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
let name;
|
|
1032
|
+
let namedBindings;
|
|
1033
|
+
if (this.isIdentifierLike(this.token)) {
|
|
1034
|
+
name = this.parseIdentifierName();
|
|
1035
|
+
if (this.at(TokenKind.Comma))
|
|
1036
|
+
this.nextToken();
|
|
1037
|
+
}
|
|
1038
|
+
if (this.at(TokenKind.Asterisk)) {
|
|
1039
|
+
this.nextToken();
|
|
1040
|
+
this.parseExpected(TokenKind.AsKeyword);
|
|
1041
|
+
const nsName = this.parseIdentifier();
|
|
1042
|
+
namedBindings = { kind: SyntaxKind.NamespaceImport, name: nsName, start: nsName.start, end: nsName.end };
|
|
1043
|
+
}
|
|
1044
|
+
else if (this.at(TokenKind.OpenBrace)) {
|
|
1045
|
+
namedBindings = this.parseNamedImports();
|
|
1046
|
+
}
|
|
1047
|
+
importClause = { kind: SyntaxKind.ImportClause, name, namedBindings, isTypeOnly, start: clauseStart, end: namedBindings?.end ?? name?.end ?? clauseStart };
|
|
1048
|
+
}
|
|
1049
|
+
this.parseExpected(TokenKind.FromKeyword);
|
|
1050
|
+
const moduleToken = this.parseExpected(TokenKind.StringLiteral);
|
|
1051
|
+
const moduleSpecifier = this.stringLiteralFromToken(moduleToken);
|
|
1052
|
+
const attributes = [];
|
|
1053
|
+
if (this.at(TokenKind.WithKeyword) || (this.at(TokenKind.Identifier) && this.token.text === "assert")) {
|
|
1054
|
+
this.nextToken();
|
|
1055
|
+
this.parseExpected(TokenKind.OpenBrace);
|
|
1056
|
+
while (!this.at(TokenKind.CloseBrace) && !this.at(TokenKind.EndOfFile)) {
|
|
1057
|
+
const key = this.parsePropertyName();
|
|
1058
|
+
this.parseExpected(TokenKind.Colon);
|
|
1059
|
+
const valueToken = this.parseExpected(TokenKind.StringLiteral);
|
|
1060
|
+
attributes.push({ name: propertyNameText(key), value: String(valueToken.value ?? "") });
|
|
1061
|
+
if (this.at(TokenKind.Comma))
|
|
1062
|
+
this.nextToken();
|
|
1063
|
+
else
|
|
1064
|
+
break;
|
|
1065
|
+
}
|
|
1066
|
+
this.parseExpected(TokenKind.CloseBrace);
|
|
1067
|
+
}
|
|
1068
|
+
this.parseSemicolon();
|
|
1069
|
+
return { kind: SyntaxKind.ImportDeclaration, importClause, moduleSpecifier, attributes, start, end: moduleSpecifier.end };
|
|
1070
|
+
}
|
|
1071
|
+
parseNamedImports() {
|
|
1072
|
+
const open = this.parseExpected(TokenKind.OpenBrace);
|
|
1073
|
+
const elements = [];
|
|
1074
|
+
while (!this.at(TokenKind.CloseBrace) && !this.at(TokenKind.EndOfFile)) {
|
|
1075
|
+
const specStart = this.token.start;
|
|
1076
|
+
let isTypeOnly = false;
|
|
1077
|
+
if (this.at(TokenKind.TypeKeyword)) {
|
|
1078
|
+
this.nextToken();
|
|
1079
|
+
isTypeOnly = true;
|
|
1080
|
+
}
|
|
1081
|
+
const first = this.parseIdentifierName();
|
|
1082
|
+
let propertyName;
|
|
1083
|
+
let name = first;
|
|
1084
|
+
if (this.at(TokenKind.AsKeyword)) {
|
|
1085
|
+
this.nextToken();
|
|
1086
|
+
propertyName = first;
|
|
1087
|
+
name = this.parseIdentifierName();
|
|
1088
|
+
}
|
|
1089
|
+
elements.push({ kind: SyntaxKind.ImportSpecifier, propertyName, name, isTypeOnly, start: specStart, end: name.end });
|
|
1090
|
+
if (this.at(TokenKind.Comma))
|
|
1091
|
+
this.nextToken();
|
|
1092
|
+
else
|
|
1093
|
+
break;
|
|
1094
|
+
}
|
|
1095
|
+
const close = this.parseExpected(TokenKind.CloseBrace);
|
|
1096
|
+
return { kind: SyntaxKind.NamedImports, elements, start: open.start, end: close.end };
|
|
1097
|
+
}
|
|
1098
|
+
parseExportDeclaration() {
|
|
1099
|
+
const start = this.parseExpected(TokenKind.ExportKeyword).start;
|
|
1100
|
+
if (this.at(TokenKind.Equals)) {
|
|
1101
|
+
this.nextToken();
|
|
1102
|
+
const expression = this.parseExpression();
|
|
1103
|
+
this.parseSemicolon();
|
|
1104
|
+
const assignment = { kind: SyntaxKind.ExportAssignment, isExportEquals: true, expression, start, end: expression.end };
|
|
1105
|
+
return assignment;
|
|
1106
|
+
}
|
|
1107
|
+
if (this.at(TokenKind.DefaultKeyword)) {
|
|
1108
|
+
this.nextToken();
|
|
1109
|
+
if (this.at(TokenKind.FunctionKeyword) || this.at(TokenKind.ClassKeyword) || this.at(TokenKind.InterfaceKeyword)) {
|
|
1110
|
+
return this.parseStatementWithModifiers([{ kind: SyntaxKind.Unknown, modifierKind: ModifierKind.Default, start, end: this.token.start }]);
|
|
1111
|
+
}
|
|
1112
|
+
const expression = this.parseAssignmentExpression();
|
|
1113
|
+
this.parseSemicolon();
|
|
1114
|
+
const assignment = { kind: SyntaxKind.ExportAssignment, isExportEquals: false, expression, start, end: expression.end };
|
|
1115
|
+
return assignment;
|
|
1116
|
+
}
|
|
1117
|
+
if (this.at(TokenKind.Asterisk)) {
|
|
1118
|
+
this.nextToken();
|
|
1119
|
+
let nsName;
|
|
1120
|
+
if (this.at(TokenKind.AsKeyword)) {
|
|
1121
|
+
this.nextToken();
|
|
1122
|
+
nsName = this.parseIdentifierName();
|
|
1123
|
+
}
|
|
1124
|
+
let moduleSpecifier;
|
|
1125
|
+
if (this.at(TokenKind.FromKeyword)) {
|
|
1126
|
+
this.nextToken();
|
|
1127
|
+
moduleSpecifier = this.stringLiteralFromToken(this.nextToken());
|
|
1128
|
+
}
|
|
1129
|
+
this.parseSemicolon();
|
|
1130
|
+
const decl = {
|
|
1131
|
+
kind: SyntaxKind.ExportDeclaration,
|
|
1132
|
+
modifiers: [],
|
|
1133
|
+
exportClause: nsName ? { kind: SyntaxKind.NamespaceImport, name: nsName, start: nsName.start, end: nsName.end } : undefined,
|
|
1134
|
+
moduleSpecifier,
|
|
1135
|
+
isTypeOnly: false,
|
|
1136
|
+
start,
|
|
1137
|
+
end: moduleSpecifier?.end ?? nsName?.end ?? start,
|
|
1138
|
+
};
|
|
1139
|
+
return decl;
|
|
1140
|
+
}
|
|
1141
|
+
if (this.at(TokenKind.OpenBrace)) {
|
|
1142
|
+
const namedExports = this.parseNamedExports();
|
|
1143
|
+
let moduleSpecifier;
|
|
1144
|
+
if (this.at(TokenKind.FromKeyword)) {
|
|
1145
|
+
this.nextToken();
|
|
1146
|
+
if (this.at(TokenKind.StringLiteral))
|
|
1147
|
+
moduleSpecifier = this.stringLiteralFromToken(this.nextToken());
|
|
1148
|
+
}
|
|
1149
|
+
this.parseSemicolon();
|
|
1150
|
+
const decl = { kind: SyntaxKind.ExportDeclaration, modifiers: [], exportClause: namedExports, moduleSpecifier, isTypeOnly: false, start, end: moduleSpecifier?.end ?? namedExports.end };
|
|
1151
|
+
return decl;
|
|
1152
|
+
}
|
|
1153
|
+
const modifiers = this.tryParseModifiers();
|
|
1154
|
+
modifiers.push({ kind: SyntaxKind.Unknown, modifierKind: ModifierKind.Export, start, end: this.token.start });
|
|
1155
|
+
return this.parseStatementWithModifiers(modifiers);
|
|
1156
|
+
}
|
|
1157
|
+
parseNamedExports() {
|
|
1158
|
+
const open = this.parseExpected(TokenKind.OpenBrace);
|
|
1159
|
+
const elements = [];
|
|
1160
|
+
while (!this.at(TokenKind.CloseBrace) && !this.at(TokenKind.EndOfFile)) {
|
|
1161
|
+
const specStart = this.token.start;
|
|
1162
|
+
let isTypeOnly = false;
|
|
1163
|
+
if (this.at(TokenKind.TypeKeyword)) {
|
|
1164
|
+
this.nextToken();
|
|
1165
|
+
isTypeOnly = true;
|
|
1166
|
+
}
|
|
1167
|
+
const first = this.parseIdentifierName();
|
|
1168
|
+
let propertyName;
|
|
1169
|
+
let name = first;
|
|
1170
|
+
if (this.at(TokenKind.AsKeyword)) {
|
|
1171
|
+
this.nextToken();
|
|
1172
|
+
propertyName = first;
|
|
1173
|
+
name = this.parseIdentifierName();
|
|
1174
|
+
}
|
|
1175
|
+
elements.push({ kind: SyntaxKind.ExportSpecifier, propertyName, name, isTypeOnly, start: specStart, end: name.end });
|
|
1176
|
+
if (this.at(TokenKind.Comma))
|
|
1177
|
+
this.nextToken();
|
|
1178
|
+
else
|
|
1179
|
+
break;
|
|
1180
|
+
}
|
|
1181
|
+
const close = this.parseExpected(TokenKind.CloseBrace);
|
|
1182
|
+
return { kind: SyntaxKind.NamedExports, elements, start: open.start, end: close.end };
|
|
1183
|
+
}
|
|
1184
|
+
stringLiteralFromToken(token) {
|
|
1185
|
+
return { kind: SyntaxKind.StringLiteral, text: token.text, raw: token.text, value: String(token.value ?? ""), start: token.start, end: token.end };
|
|
1186
|
+
}
|
|
1187
|
+
// -- state save / restore (for speculative parsing) -----------------------
|
|
1188
|
+
saveState() {
|
|
1189
|
+
return { index: this.index, tokens: this.tokens.slice(), pos: this.scanner.position, consumed: this.consumed };
|
|
1190
|
+
}
|
|
1191
|
+
restoreState(state) {
|
|
1192
|
+
this.index = state.index;
|
|
1193
|
+
this.tokens = state.tokens;
|
|
1194
|
+
this.scanner.seek(state.pos);
|
|
1195
|
+
this.consumed = state.consumed;
|
|
1196
|
+
}
|
|
1197
|
+
// -- expressions ----------------------------------------------------------
|
|
1198
|
+
parseExpression(noIn = false) {
|
|
1199
|
+
return this.parseAssignmentExpression(noIn);
|
|
1200
|
+
}
|
|
1201
|
+
parseAssignmentExpression(noIn = false) {
|
|
1202
|
+
const arrow = this.tryParseArrowFunction();
|
|
1203
|
+
if (arrow)
|
|
1204
|
+
return arrow;
|
|
1205
|
+
const start = this.token.start;
|
|
1206
|
+
const left = this.parseConditionalExpression(noIn);
|
|
1207
|
+
const token = this.token;
|
|
1208
|
+
const op = assignmentOperator(token.kind);
|
|
1209
|
+
if (op) {
|
|
1210
|
+
if (!isAssignmentTarget(left)) {
|
|
1211
|
+
this.error(DiagnosticCode.InvalidAssignmentTarget, "Invalid assignment target", token);
|
|
1212
|
+
}
|
|
1213
|
+
this.nextToken();
|
|
1214
|
+
const right = this.parseAssignmentExpression(noIn);
|
|
1215
|
+
const assignment = {
|
|
1216
|
+
kind: SyntaxKind.BinaryExpression,
|
|
1217
|
+
left,
|
|
1218
|
+
operator: op,
|
|
1219
|
+
right,
|
|
1220
|
+
operatorToken: token,
|
|
1221
|
+
start,
|
|
1222
|
+
end: right.end,
|
|
1223
|
+
};
|
|
1224
|
+
return assignment;
|
|
1225
|
+
}
|
|
1226
|
+
return left;
|
|
1227
|
+
}
|
|
1228
|
+
tryParseArrowFunction() {
|
|
1229
|
+
const start = this.token.start;
|
|
1230
|
+
let flags = NodeFlags.None;
|
|
1231
|
+
let async = false;
|
|
1232
|
+
let cursor = 0;
|
|
1233
|
+
if (this.at(TokenKind.AsyncKeyword) && !this.lookAhead(1).precededByLineBreak) {
|
|
1234
|
+
const next = this.lookAhead(1);
|
|
1235
|
+
if (next.kind === TokenKind.OpenParen || this.isIdentifierLike(next))
|
|
1236
|
+
async = true;
|
|
1237
|
+
}
|
|
1238
|
+
if (async)
|
|
1239
|
+
cursor = 1;
|
|
1240
|
+
const afterTypeParams = this.countTypeParameterTokens(cursor);
|
|
1241
|
+
if (afterTypeParams >= 0)
|
|
1242
|
+
cursor = afterTypeParams;
|
|
1243
|
+
const first = this.lookAhead(cursor);
|
|
1244
|
+
if (first.kind === TokenKind.OpenParen) {
|
|
1245
|
+
const closeIndex = this.findMatchingParen(cursor);
|
|
1246
|
+
if (closeIndex < 0)
|
|
1247
|
+
return undefined;
|
|
1248
|
+
let arrowIndex = closeIndex + 1;
|
|
1249
|
+
if (this.lookAhead(arrowIndex).kind === TokenKind.Colon) {
|
|
1250
|
+
arrowIndex = this.skipTypeTokens(arrowIndex + 1);
|
|
1251
|
+
if (arrowIndex < 0)
|
|
1252
|
+
return undefined;
|
|
1253
|
+
}
|
|
1254
|
+
if (this.lookAhead(arrowIndex).kind !== TokenKind.EqualsGreaterThan)
|
|
1255
|
+
return undefined;
|
|
1256
|
+
}
|
|
1257
|
+
else if (this.isIdentifierLike(first)) {
|
|
1258
|
+
if (this.lookAhead(cursor + 1).kind !== TokenKind.EqualsGreaterThan)
|
|
1259
|
+
return undefined;
|
|
1260
|
+
}
|
|
1261
|
+
else {
|
|
1262
|
+
return undefined;
|
|
1263
|
+
}
|
|
1264
|
+
if (async) {
|
|
1265
|
+
this.nextToken();
|
|
1266
|
+
flags |= NodeFlags.Async;
|
|
1267
|
+
}
|
|
1268
|
+
const typeParams = this.at(TokenKind.LessThan) ? this.parseTypeParameters() : [];
|
|
1269
|
+
let parameters;
|
|
1270
|
+
if (this.at(TokenKind.OpenParen)) {
|
|
1271
|
+
parameters = this.parseParameters();
|
|
1272
|
+
}
|
|
1273
|
+
else {
|
|
1274
|
+
const paramStart = this.token.start;
|
|
1275
|
+
const name = this.parseIdentifierName();
|
|
1276
|
+
parameters = [{ kind: SyntaxKind.Parameter, name, modifiers: [], dotDotDotToken: false, questionToken: false, start: paramStart, end: name.end }];
|
|
1277
|
+
}
|
|
1278
|
+
let returnType;
|
|
1279
|
+
if (this.at(TokenKind.Colon)) {
|
|
1280
|
+
this.nextToken();
|
|
1281
|
+
returnType = this.parseReturnType();
|
|
1282
|
+
}
|
|
1283
|
+
this.parseExpected(TokenKind.EqualsGreaterThan);
|
|
1284
|
+
const body = this.at(TokenKind.OpenBrace) ? this.parseBlock() : this.parseAssignmentExpression();
|
|
1285
|
+
return { kind: SyntaxKind.ArrowFunction, typeParameters: typeParams, parameters, returnType, body, flags, start, end: body.end };
|
|
1286
|
+
}
|
|
1287
|
+
countTypeParameterTokens(cursor) {
|
|
1288
|
+
if (this.lookAhead(cursor).kind !== TokenKind.LessThan)
|
|
1289
|
+
return -1;
|
|
1290
|
+
let depth = 0;
|
|
1291
|
+
for (let i = cursor;; i++) {
|
|
1292
|
+
const t = this.lookAhead(i);
|
|
1293
|
+
if (t.kind === TokenKind.EndOfFile)
|
|
1294
|
+
return -1;
|
|
1295
|
+
if (t.kind === TokenKind.LessThan)
|
|
1296
|
+
depth++;
|
|
1297
|
+
else if (t.kind === TokenKind.GreaterThan) {
|
|
1298
|
+
depth--;
|
|
1299
|
+
if (depth === 0)
|
|
1300
|
+
return i + 1;
|
|
1301
|
+
}
|
|
1302
|
+
else if (t.kind === TokenKind.GreaterThanGreaterThan || t.kind === TokenKind.GreaterThanGreaterThanGreaterThan) {
|
|
1303
|
+
depth -= t.kind === TokenKind.GreaterThanGreaterThan ? 2 : 3;
|
|
1304
|
+
if (depth <= 0)
|
|
1305
|
+
return i + 1;
|
|
1306
|
+
}
|
|
1307
|
+
else if (t.kind === TokenKind.Semicolon) {
|
|
1308
|
+
return -1;
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
findMatchingParen(cursor) {
|
|
1313
|
+
let depth = 0;
|
|
1314
|
+
for (let i = cursor;; i++) {
|
|
1315
|
+
const t = this.lookAhead(i);
|
|
1316
|
+
if (t.kind === TokenKind.EndOfFile)
|
|
1317
|
+
return -1;
|
|
1318
|
+
if (t.kind === TokenKind.OpenParen)
|
|
1319
|
+
depth++;
|
|
1320
|
+
else if (t.kind === TokenKind.CloseParen) {
|
|
1321
|
+
depth--;
|
|
1322
|
+
if (depth === 0)
|
|
1323
|
+
return i;
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
skipTypeTokens(startIndex) {
|
|
1328
|
+
let depth = 0;
|
|
1329
|
+
for (let i = startIndex;; i++) {
|
|
1330
|
+
const t = this.lookAhead(i);
|
|
1331
|
+
if (t.kind === TokenKind.EndOfFile)
|
|
1332
|
+
return -1;
|
|
1333
|
+
if (t.kind === TokenKind.OpenParen || t.kind === TokenKind.OpenBracket || t.kind === TokenKind.OpenBrace)
|
|
1334
|
+
depth++;
|
|
1335
|
+
else if (t.kind === TokenKind.CloseParen || t.kind === TokenKind.CloseBracket || t.kind === TokenKind.CloseBrace)
|
|
1336
|
+
depth--;
|
|
1337
|
+
else if (t.kind === TokenKind.EqualsGreaterThan && depth === 0)
|
|
1338
|
+
return i;
|
|
1339
|
+
else if (t.kind === TokenKind.Semicolon && depth === 0)
|
|
1340
|
+
return -1;
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
parseConditionalExpression(noIn) {
|
|
1344
|
+
const start = this.token.start;
|
|
1345
|
+
const condition = this.parseBinaryExpression(0, noIn);
|
|
1346
|
+
if (this.at(TokenKind.Question)) {
|
|
1347
|
+
this.nextToken();
|
|
1348
|
+
const whenTrue = this.parseAssignmentExpression();
|
|
1349
|
+
this.parseExpected(TokenKind.Colon);
|
|
1350
|
+
const whenFalse = this.parseAssignmentExpression(noIn);
|
|
1351
|
+
const node = { kind: SyntaxKind.ConditionalExpression, condition, whenTrue, whenFalse, start, end: whenFalse.end };
|
|
1352
|
+
return node;
|
|
1353
|
+
}
|
|
1354
|
+
return condition;
|
|
1355
|
+
}
|
|
1356
|
+
parseBinaryExpression(minPrecedence, noIn) {
|
|
1357
|
+
let left = this.parseUnaryExpression();
|
|
1358
|
+
for (;;) {
|
|
1359
|
+
const token = this.token;
|
|
1360
|
+
// `expr as Type` / `expr satisfies Type` bind at the relational level.
|
|
1361
|
+
if (token.kind === TokenKind.AsKeyword || token.kind === TokenKind.SatisfiesKeyword) {
|
|
1362
|
+
if (AS_PRECEDENCE < minPrecedence)
|
|
1363
|
+
break;
|
|
1364
|
+
this.nextToken();
|
|
1365
|
+
const type = this.parseType();
|
|
1366
|
+
left =
|
|
1367
|
+
token.kind === TokenKind.AsKeyword
|
|
1368
|
+
? { kind: SyntaxKind.AsExpression, expression: left, type, start: left.start, end: type.end }
|
|
1369
|
+
: { kind: SyntaxKind.SatisfiesExpression, expression: left, type, start: left.start, end: type.end };
|
|
1370
|
+
continue;
|
|
1371
|
+
}
|
|
1372
|
+
if (token.kind === TokenKind.InKeyword && noIn)
|
|
1373
|
+
break;
|
|
1374
|
+
const op = binaryOperator(token.kind);
|
|
1375
|
+
if (!op)
|
|
1376
|
+
break;
|
|
1377
|
+
const precedence = binaryPrecedence(op);
|
|
1378
|
+
if (precedence < minPrecedence)
|
|
1379
|
+
break;
|
|
1380
|
+
const nextMin = op === BinaryOperator.Exponent ? precedence : precedence + 1;
|
|
1381
|
+
this.nextToken();
|
|
1382
|
+
const right = this.parseBinaryExpression(nextMin, noIn);
|
|
1383
|
+
const binary = { kind: SyntaxKind.BinaryExpression, left, operator: op, right, operatorToken: token, start: left.start, end: right.end };
|
|
1384
|
+
left = binary;
|
|
1385
|
+
}
|
|
1386
|
+
return left;
|
|
1387
|
+
}
|
|
1388
|
+
parseUnaryExpression() {
|
|
1389
|
+
const token = this.token;
|
|
1390
|
+
const prefix = prefixUnaryOperator(token.kind);
|
|
1391
|
+
if (prefix) {
|
|
1392
|
+
this.nextToken();
|
|
1393
|
+
if (prefix === PrefixUnaryOperator.Await) {
|
|
1394
|
+
const expression = this.parseUnaryExpression();
|
|
1395
|
+
return { kind: SyntaxKind.AwaitExpression, expression, start: token.start, end: expression.end };
|
|
1396
|
+
}
|
|
1397
|
+
if (prefix === PrefixUnaryOperator.Delete) {
|
|
1398
|
+
const expression = this.parseUnaryExpression();
|
|
1399
|
+
return { kind: SyntaxKind.DeleteExpression, expression, start: token.start, end: expression.end };
|
|
1400
|
+
}
|
|
1401
|
+
if (prefix === PrefixUnaryOperator.Void) {
|
|
1402
|
+
const expression = this.parseUnaryExpression();
|
|
1403
|
+
return { kind: SyntaxKind.VoidExpression, expression, start: token.start, end: expression.end };
|
|
1404
|
+
}
|
|
1405
|
+
if (prefix === PrefixUnaryOperator.TypeOf) {
|
|
1406
|
+
const expression = this.parseUnaryExpression();
|
|
1407
|
+
return { kind: SyntaxKind.TypeOfExpression, expression, start: token.start, end: expression.end };
|
|
1408
|
+
}
|
|
1409
|
+
const operand = this.parseUnaryExpression();
|
|
1410
|
+
return { kind: SyntaxKind.PrefixUnaryExpression, operator: prefix, operand, start: token.start, end: operand.end };
|
|
1411
|
+
}
|
|
1412
|
+
if (token.kind === TokenKind.YieldKeyword) {
|
|
1413
|
+
this.nextToken();
|
|
1414
|
+
let delegate = false;
|
|
1415
|
+
let expression;
|
|
1416
|
+
if (this.at(TokenKind.Asterisk)) {
|
|
1417
|
+
this.nextToken();
|
|
1418
|
+
delegate = true;
|
|
1419
|
+
}
|
|
1420
|
+
if (!this.canInsertSemicolon() && !this.at(TokenKind.Semicolon) && !this.at(TokenKind.CloseParen)) {
|
|
1421
|
+
expression = this.parseAssignmentExpression();
|
|
1422
|
+
}
|
|
1423
|
+
return { kind: SyntaxKind.YieldExpression, expression, delegate, start: token.start, end: expression?.end ?? token.end };
|
|
1424
|
+
}
|
|
1425
|
+
if (token.kind === TokenKind.LessThan) {
|
|
1426
|
+
const assertion = this.tryParse(() => {
|
|
1427
|
+
const type = this.parseType();
|
|
1428
|
+
const expression = this.parseUnaryExpression();
|
|
1429
|
+
return { kind: SyntaxKind.AsExpression, expression, type, start: token.start, end: expression.end };
|
|
1430
|
+
});
|
|
1431
|
+
if (assertion)
|
|
1432
|
+
return assertion;
|
|
1433
|
+
}
|
|
1434
|
+
return this.parsePostfixExpression();
|
|
1435
|
+
}
|
|
1436
|
+
parsePostfixExpression() {
|
|
1437
|
+
const start = this.token.start;
|
|
1438
|
+
const expression = this.parseLeftHandSideExpression();
|
|
1439
|
+
const token = this.token;
|
|
1440
|
+
if ((token.kind === TokenKind.PlusPlus || token.kind === TokenKind.MinusMinus) && !token.precededByLineBreak) {
|
|
1441
|
+
this.nextToken();
|
|
1442
|
+
const operator = token.kind === TokenKind.PlusPlus ? PostfixUnaryOperator.PlusPlus : PostfixUnaryOperator.MinusMinus;
|
|
1443
|
+
return { kind: SyntaxKind.PostfixUnaryExpression, operator, operand: expression, start, end: token.end };
|
|
1444
|
+
}
|
|
1445
|
+
return expression;
|
|
1446
|
+
}
|
|
1447
|
+
parseLeftHandSideExpression() {
|
|
1448
|
+
const expression = this.at(TokenKind.NewKeyword) ? this.parseNewExpression() : this.parsePrimaryExpression();
|
|
1449
|
+
return this.parseCallAndMemberTail(expression);
|
|
1450
|
+
}
|
|
1451
|
+
parseNewExpression() {
|
|
1452
|
+
const start = this.parseExpected(TokenKind.NewKeyword).start;
|
|
1453
|
+
const callee = this.parseMemberOnlyExpression();
|
|
1454
|
+
const typeArguments = this.at(TokenKind.LessThan) ? this.parseTypeArguments() : [];
|
|
1455
|
+
const args = this.at(TokenKind.OpenParen) ? this.parseArguments() : [];
|
|
1456
|
+
return { kind: SyntaxKind.NewExpression, expression: callee, typeArguments, arguments: args, start, end: args[args.length - 1]?.end ?? callee.end };
|
|
1457
|
+
}
|
|
1458
|
+
parseMemberOnlyExpression() {
|
|
1459
|
+
let expression = this.parsePrimaryExpression();
|
|
1460
|
+
for (;;) {
|
|
1461
|
+
if (this.at(TokenKind.Dot) || this.at(TokenKind.QuestionDot)) {
|
|
1462
|
+
expression = this.parsePropertyAccess(expression);
|
|
1463
|
+
}
|
|
1464
|
+
else if (this.at(TokenKind.OpenBracket)) {
|
|
1465
|
+
this.nextToken();
|
|
1466
|
+
const argument = this.parseExpression();
|
|
1467
|
+
const close = this.parseExpected(TokenKind.CloseBracket);
|
|
1468
|
+
expression = { kind: SyntaxKind.ElementAccessExpression, expression, argumentExpression: argument, optional: false, start: expression.start, end: close.end };
|
|
1469
|
+
}
|
|
1470
|
+
else {
|
|
1471
|
+
break;
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
return expression;
|
|
1475
|
+
}
|
|
1476
|
+
parsePrimaryExpression() {
|
|
1477
|
+
const token = this.token;
|
|
1478
|
+
switch (token.kind) {
|
|
1479
|
+
case TokenKind.NumericLiteral: {
|
|
1480
|
+
this.nextToken();
|
|
1481
|
+
const node = { kind: SyntaxKind.NumericLiteral, text: token.text, value: Number(token.value ?? 0), start: token.start, end: token.end };
|
|
1482
|
+
return node;
|
|
1483
|
+
}
|
|
1484
|
+
case TokenKind.BigIntLiteral:
|
|
1485
|
+
this.nextToken();
|
|
1486
|
+
return { kind: SyntaxKind.BigIntLiteral, text: token.text, value: BigInt(token.value ?? 0n), start: token.start, end: token.end };
|
|
1487
|
+
case TokenKind.StringLiteral:
|
|
1488
|
+
this.nextToken();
|
|
1489
|
+
return this.stringLiteralFromToken(token);
|
|
1490
|
+
case TokenKind.TrueKeyword:
|
|
1491
|
+
this.nextToken();
|
|
1492
|
+
return { kind: SyntaxKind.TrueKeyword, value: true, start: token.start, end: token.end };
|
|
1493
|
+
case TokenKind.FalseKeyword:
|
|
1494
|
+
this.nextToken();
|
|
1495
|
+
return { kind: SyntaxKind.FalseKeyword, value: false, start: token.start, end: token.end };
|
|
1496
|
+
case TokenKind.NullKeyword:
|
|
1497
|
+
this.nextToken();
|
|
1498
|
+
return { kind: SyntaxKind.NullKeyword, start: token.start, end: token.end };
|
|
1499
|
+
case TokenKind.UndefinedKeyword:
|
|
1500
|
+
this.nextToken();
|
|
1501
|
+
return { kind: SyntaxKind.UndefinedKeyword, start: token.start, end: token.end };
|
|
1502
|
+
case TokenKind.ThisKeyword:
|
|
1503
|
+
this.nextToken();
|
|
1504
|
+
return { kind: SyntaxKind.ThisKeyword, start: token.start, end: token.end };
|
|
1505
|
+
case TokenKind.OpenParen: {
|
|
1506
|
+
this.nextToken();
|
|
1507
|
+
const expression = this.parseExpression();
|
|
1508
|
+
const close = this.parseExpected(TokenKind.CloseParen);
|
|
1509
|
+
const paren = { kind: SyntaxKind.ParenthesizedExpression, expression, start: token.start, end: close.end };
|
|
1510
|
+
return paren;
|
|
1511
|
+
}
|
|
1512
|
+
case TokenKind.OpenBracket:
|
|
1513
|
+
return this.parseArrayLiteral();
|
|
1514
|
+
case TokenKind.OpenBrace:
|
|
1515
|
+
return this.parseObjectLiteral();
|
|
1516
|
+
case TokenKind.FunctionKeyword:
|
|
1517
|
+
return this.parseFunctionExpression();
|
|
1518
|
+
case TokenKind.ClassKeyword:
|
|
1519
|
+
return this.parseClassExpression();
|
|
1520
|
+
case TokenKind.AsyncKeyword:
|
|
1521
|
+
if (this.atAhead(1, TokenKind.FunctionKeyword) && !this.lookAhead(1).precededByLineBreak)
|
|
1522
|
+
return this.parseFunctionExpression();
|
|
1523
|
+
break;
|
|
1524
|
+
case TokenKind.Backtick:
|
|
1525
|
+
case TokenKind.TemplateHead:
|
|
1526
|
+
case TokenKind.NoSubstitutionTemplateLiteral:
|
|
1527
|
+
return this.parseTemplateLiteral();
|
|
1528
|
+
case TokenKind.Slash:
|
|
1529
|
+
return this.parseRegularExpression();
|
|
1530
|
+
default:
|
|
1531
|
+
break;
|
|
1532
|
+
}
|
|
1533
|
+
if (this.isIdentifierLike(token))
|
|
1534
|
+
return this.parseIdentifierName();
|
|
1535
|
+
this.error(DiagnosticCode.UnexpectedToken, `Unexpected token '${token.text || token.kind}'`);
|
|
1536
|
+
if (token.kind !== TokenKind.EndOfFile)
|
|
1537
|
+
this.nextToken();
|
|
1538
|
+
return { kind: SyntaxKind.Identifier, text: "<error>", start: token.start, end: token.end };
|
|
1539
|
+
}
|
|
1540
|
+
parseCallAndMemberTail(base) {
|
|
1541
|
+
let expression = base;
|
|
1542
|
+
for (;;) {
|
|
1543
|
+
const token = this.token;
|
|
1544
|
+
if (token.kind === TokenKind.Dot || token.kind === TokenKind.QuestionDot) {
|
|
1545
|
+
expression = this.parsePropertyAccess(expression);
|
|
1546
|
+
continue;
|
|
1547
|
+
}
|
|
1548
|
+
if (token.kind === TokenKind.OpenBracket) {
|
|
1549
|
+
this.nextToken();
|
|
1550
|
+
const argument = this.parseExpression();
|
|
1551
|
+
const close = this.parseExpected(TokenKind.CloseBracket);
|
|
1552
|
+
expression = { kind: SyntaxKind.ElementAccessExpression, expression, argumentExpression: argument, optional: false, start: expression.start, end: close.end };
|
|
1553
|
+
continue;
|
|
1554
|
+
}
|
|
1555
|
+
if (token.kind === TokenKind.OpenParen) {
|
|
1556
|
+
const args = this.parseArguments();
|
|
1557
|
+
const call = { kind: SyntaxKind.CallExpression, expression, typeArguments: [], arguments: args, optional: false, start: expression.start, end: this.previousEnd(args, expression.end) };
|
|
1558
|
+
expression = call;
|
|
1559
|
+
continue;
|
|
1560
|
+
}
|
|
1561
|
+
if (token.kind === TokenKind.LessThan) {
|
|
1562
|
+
const call = this.tryParse(() => {
|
|
1563
|
+
const typeArguments = this.parseTypeArguments();
|
|
1564
|
+
if (!this.at(TokenKind.OpenParen))
|
|
1565
|
+
throw new SpeculationError();
|
|
1566
|
+
const args = this.parseArguments();
|
|
1567
|
+
return { kind: SyntaxKind.CallExpression, expression, typeArguments, arguments: args, optional: false, start: expression.start, end: this.previousEnd(args, this.token.start) };
|
|
1568
|
+
});
|
|
1569
|
+
if (call) {
|
|
1570
|
+
expression = call;
|
|
1571
|
+
continue;
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
if (this.at(TokenKind.Backtick) || this.at(TokenKind.TemplateHead) || this.at(TokenKind.NoSubstitutionTemplateLiteral)) {
|
|
1575
|
+
const template = this.parseTemplateLiteral();
|
|
1576
|
+
expression = { kind: SyntaxKind.TaggedTemplateExpression, tag: expression, template, start: expression.start, end: template.end };
|
|
1577
|
+
continue;
|
|
1578
|
+
}
|
|
1579
|
+
return expression;
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
previousEnd(args, fallback) {
|
|
1583
|
+
return args[args.length - 1]?.end ?? fallback;
|
|
1584
|
+
}
|
|
1585
|
+
parsePropertyAccess(expression) {
|
|
1586
|
+
const token = this.nextToken();
|
|
1587
|
+
const optional = token.kind === TokenKind.QuestionDot;
|
|
1588
|
+
if (optional && this.at(TokenKind.OpenParen)) {
|
|
1589
|
+
const args = this.parseArguments();
|
|
1590
|
+
return { kind: SyntaxKind.CallExpression, expression, typeArguments: [], arguments: args, optional: true, start: expression.start, end: this.previousEnd(args, token.start) };
|
|
1591
|
+
}
|
|
1592
|
+
if (optional && this.at(TokenKind.OpenBracket)) {
|
|
1593
|
+
this.nextToken();
|
|
1594
|
+
const argument = this.parseExpression();
|
|
1595
|
+
const close = this.parseExpected(TokenKind.CloseBracket);
|
|
1596
|
+
return { kind: SyntaxKind.ElementAccessExpression, expression, argumentExpression: argument, optional: true, start: expression.start, end: close.end };
|
|
1597
|
+
}
|
|
1598
|
+
const name = this.parseIdentifierName();
|
|
1599
|
+
return { kind: SyntaxKind.PropertyAccessExpression, expression, name, optional, start: expression.start, end: name.end };
|
|
1600
|
+
}
|
|
1601
|
+
parseArguments() {
|
|
1602
|
+
this.parseExpected(TokenKind.OpenParen);
|
|
1603
|
+
const args = [];
|
|
1604
|
+
while (!this.at(TokenKind.CloseParen) && !this.at(TokenKind.EndOfFile)) {
|
|
1605
|
+
if (this.at(TokenKind.DotDotDot)) {
|
|
1606
|
+
const start = this.nextToken().start;
|
|
1607
|
+
const expression = this.parseAssignmentExpression();
|
|
1608
|
+
args.push({ kind: SyntaxKind.SpreadElement, expression, start, end: expression.end });
|
|
1609
|
+
}
|
|
1610
|
+
else {
|
|
1611
|
+
args.push(this.parseAssignmentExpression());
|
|
1612
|
+
}
|
|
1613
|
+
if (this.at(TokenKind.Comma))
|
|
1614
|
+
this.nextToken();
|
|
1615
|
+
else
|
|
1616
|
+
break;
|
|
1617
|
+
}
|
|
1618
|
+
this.parseExpected(TokenKind.CloseParen);
|
|
1619
|
+
return args;
|
|
1620
|
+
}
|
|
1621
|
+
parseArrayLiteral() {
|
|
1622
|
+
const open = this.parseExpected(TokenKind.OpenBracket);
|
|
1623
|
+
const elements = [];
|
|
1624
|
+
while (!this.at(TokenKind.CloseBracket) && !this.at(TokenKind.EndOfFile)) {
|
|
1625
|
+
if (this.at(TokenKind.Comma)) {
|
|
1626
|
+
// Elision: represent as an undefined literal hole.
|
|
1627
|
+
elements.push({ kind: SyntaxKind.UndefinedKeyword, start: this.token.start, end: this.token.start });
|
|
1628
|
+
this.nextToken();
|
|
1629
|
+
continue;
|
|
1630
|
+
}
|
|
1631
|
+
if (this.at(TokenKind.DotDotDot)) {
|
|
1632
|
+
const start = this.nextToken().start;
|
|
1633
|
+
const expression = this.parseAssignmentExpression();
|
|
1634
|
+
elements.push({ kind: SyntaxKind.SpreadElement, expression, start, end: expression.end });
|
|
1635
|
+
}
|
|
1636
|
+
else {
|
|
1637
|
+
elements.push(this.parseAssignmentExpression());
|
|
1638
|
+
}
|
|
1639
|
+
if (this.at(TokenKind.Comma))
|
|
1640
|
+
this.nextToken();
|
|
1641
|
+
else
|
|
1642
|
+
break;
|
|
1643
|
+
}
|
|
1644
|
+
const close = this.parseExpected(TokenKind.CloseBracket);
|
|
1645
|
+
return { kind: SyntaxKind.ArrayLiteralExpression, elements, start: open.start, end: close.end };
|
|
1646
|
+
}
|
|
1647
|
+
parseObjectLiteral() {
|
|
1648
|
+
const open = this.parseExpected(TokenKind.OpenBrace);
|
|
1649
|
+
const properties = [];
|
|
1650
|
+
while (!this.at(TokenKind.CloseBrace) && !this.at(TokenKind.EndOfFile)) {
|
|
1651
|
+
if (this.at(TokenKind.DotDotDot)) {
|
|
1652
|
+
const start = this.nextToken().start;
|
|
1653
|
+
const expression = this.parseAssignmentExpression();
|
|
1654
|
+
properties.push({ kind: SyntaxKind.SpreadElement, expression, start, end: expression.end });
|
|
1655
|
+
}
|
|
1656
|
+
else {
|
|
1657
|
+
const start = this.token.start;
|
|
1658
|
+
const name = this.parsePropertyName();
|
|
1659
|
+
if (this.at(TokenKind.Colon)) {
|
|
1660
|
+
this.nextToken();
|
|
1661
|
+
const initializer = this.parseAssignmentExpression();
|
|
1662
|
+
properties.push({ kind: SyntaxKind.PropertyAssignment, name, initializer, start, end: initializer.end });
|
|
1663
|
+
}
|
|
1664
|
+
else if (this.at(TokenKind.OpenParen) || this.at(TokenKind.LessThan)) {
|
|
1665
|
+
const typeParameters = this.at(TokenKind.LessThan) ? this.parseTypeParameters() : [];
|
|
1666
|
+
const parameters = this.parseParameters();
|
|
1667
|
+
let returnType;
|
|
1668
|
+
if (this.at(TokenKind.Colon)) {
|
|
1669
|
+
this.nextToken();
|
|
1670
|
+
returnType = this.parseReturnType();
|
|
1671
|
+
}
|
|
1672
|
+
const body = this.parseBlock();
|
|
1673
|
+
const fn = { kind: SyntaxKind.FunctionExpression, typeParameters, parameters, returnType, body, flags: NodeFlags.None, start, end: body.end };
|
|
1674
|
+
properties.push({ kind: SyntaxKind.PropertyAssignment, name, initializer: fn, start, end: body.end });
|
|
1675
|
+
}
|
|
1676
|
+
else if (name.kind === SyntaxKind.Identifier) {
|
|
1677
|
+
const shorthand = { kind: SyntaxKind.ShorthandPropertyAssignment, name, start, end: name.end };
|
|
1678
|
+
properties.push(shorthand);
|
|
1679
|
+
}
|
|
1680
|
+
else {
|
|
1681
|
+
this.error(DiagnosticCode.UnexpectedToken, "Invalid object literal member");
|
|
1682
|
+
this.nextToken();
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
if (this.at(TokenKind.Comma))
|
|
1686
|
+
this.nextToken();
|
|
1687
|
+
else
|
|
1688
|
+
break;
|
|
1689
|
+
}
|
|
1690
|
+
const close = this.parseExpected(TokenKind.CloseBrace);
|
|
1691
|
+
return { kind: SyntaxKind.ObjectLiteralExpression, properties, start: open.start, end: close.end };
|
|
1692
|
+
}
|
|
1693
|
+
parseFunctionExpression() {
|
|
1694
|
+
const start = this.parseExpected(TokenKind.FunctionKeyword).start;
|
|
1695
|
+
let flags = NodeFlags.None;
|
|
1696
|
+
if (this.at(TokenKind.Asterisk)) {
|
|
1697
|
+
this.nextToken();
|
|
1698
|
+
flags |= NodeFlags.Generator;
|
|
1699
|
+
}
|
|
1700
|
+
let name;
|
|
1701
|
+
if (this.at(TokenKind.Identifier))
|
|
1702
|
+
name = this.parseIdentifier();
|
|
1703
|
+
const typeParameters = this.parseTypeParameters();
|
|
1704
|
+
const parameters = this.parseParameters();
|
|
1705
|
+
let returnType;
|
|
1706
|
+
if (this.at(TokenKind.Colon)) {
|
|
1707
|
+
this.nextToken();
|
|
1708
|
+
returnType = this.parseReturnType();
|
|
1709
|
+
}
|
|
1710
|
+
const body = this.parseBlock();
|
|
1711
|
+
return { kind: SyntaxKind.FunctionExpression, name, typeParameters, parameters, returnType, body, flags, start, end: body.end };
|
|
1712
|
+
}
|
|
1713
|
+
parseClassExpression() {
|
|
1714
|
+
const start = this.parseExpected(TokenKind.ClassKeyword).start;
|
|
1715
|
+
let name;
|
|
1716
|
+
if (this.at(TokenKind.Identifier))
|
|
1717
|
+
name = this.parseIdentifier();
|
|
1718
|
+
const typeParameters = this.parseTypeParameters();
|
|
1719
|
+
const heritage = this.parseHeritageClauses();
|
|
1720
|
+
const members = this.parseClassMembers();
|
|
1721
|
+
return { kind: SyntaxKind.ClassExpression, name, typeParameters, heritage, members, start, end: members[members.length - 1]?.end ?? start };
|
|
1722
|
+
}
|
|
1723
|
+
parseTemplateLiteral() {
|
|
1724
|
+
const startToken = this.token;
|
|
1725
|
+
if (startToken.kind === TokenKind.NoSubstitutionTemplateLiteral) {
|
|
1726
|
+
this.nextToken();
|
|
1727
|
+
return { kind: SyntaxKind.NoSubstitutionTemplateLiteral, text: startToken.text, value: String(startToken.value ?? ""), start: startToken.start, end: startToken.end };
|
|
1728
|
+
}
|
|
1729
|
+
if (startToken.kind === TokenKind.Backtick) {
|
|
1730
|
+
// Re-scan from the backtick so the scanner produces a TemplateHead.
|
|
1731
|
+
this.scanner.seek(startToken.start);
|
|
1732
|
+
this.tokens = [this.scanner.nextToken()];
|
|
1733
|
+
this.index = 0;
|
|
1734
|
+
return this.parseTemplateLiteral();
|
|
1735
|
+
}
|
|
1736
|
+
const head = String(startToken.value ?? "");
|
|
1737
|
+
this.nextToken();
|
|
1738
|
+
const spans = [];
|
|
1739
|
+
for (;;) {
|
|
1740
|
+
const expression = this.parseExpression();
|
|
1741
|
+
if (!this.at(TokenKind.CloseBrace)) {
|
|
1742
|
+
this.error(DiagnosticCode.UnterminatedTemplate, "Expected '}' to close template substitution");
|
|
1743
|
+
return { kind: SyntaxKind.TemplateLiteral, head, spans, start: startToken.start, end: this.token.end };
|
|
1744
|
+
}
|
|
1745
|
+
// The `}` terminates a substitution; re-read it as template text without
|
|
1746
|
+
// first scanning the following (template) characters as normal tokens.
|
|
1747
|
+
const closeStart = this.token.start;
|
|
1748
|
+
this.tokens = [this.scanner.continueTemplate(closeStart)];
|
|
1749
|
+
this.index = 0;
|
|
1750
|
+
const literalToken = this.token;
|
|
1751
|
+
if (literalToken.kind === TokenKind.NoSubstitutionTemplateLiteral) {
|
|
1752
|
+
this.nextToken();
|
|
1753
|
+
spans.push({ kind: SyntaxKind.TemplateSpan, expression, literal: String(literalToken.value ?? ""), isTail: true, start: expression.start, end: literalToken.end });
|
|
1754
|
+
return { kind: SyntaxKind.TemplateLiteral, head, spans, start: startToken.start, end: literalToken.end };
|
|
1755
|
+
}
|
|
1756
|
+
if (literalToken.kind === TokenKind.TemplateHead) {
|
|
1757
|
+
this.nextToken();
|
|
1758
|
+
spans.push({ kind: SyntaxKind.TemplateSpan, expression, literal: String(literalToken.value ?? ""), isTail: false, start: expression.start, end: literalToken.end });
|
|
1759
|
+
continue;
|
|
1760
|
+
}
|
|
1761
|
+
this.error(DiagnosticCode.UnterminatedTemplate, "Unterminated template literal", literalToken);
|
|
1762
|
+
return { kind: SyntaxKind.TemplateLiteral, head, spans, start: startToken.start, end: literalToken.end };
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
parseRegularExpression() {
|
|
1766
|
+
const token = this.token;
|
|
1767
|
+
const text = this.source.text;
|
|
1768
|
+
let i = token.start + 1;
|
|
1769
|
+
let inClass = false;
|
|
1770
|
+
let pattern = "";
|
|
1771
|
+
for (; i < text.length; i++) {
|
|
1772
|
+
const ch = text[i];
|
|
1773
|
+
if (ch === "\\") {
|
|
1774
|
+
pattern += ch + (text[i + 1] ?? "");
|
|
1775
|
+
i++;
|
|
1776
|
+
continue;
|
|
1777
|
+
}
|
|
1778
|
+
if (ch === "[")
|
|
1779
|
+
inClass = true;
|
|
1780
|
+
else if (ch === "]")
|
|
1781
|
+
inClass = false;
|
|
1782
|
+
else if (ch === "/" && !inClass)
|
|
1783
|
+
break;
|
|
1784
|
+
else if (ch === "\n")
|
|
1785
|
+
break;
|
|
1786
|
+
pattern += ch;
|
|
1787
|
+
}
|
|
1788
|
+
if (text[i] !== "/") {
|
|
1789
|
+
this.error(DiagnosticCode.InvalidCharacter, "Unterminated regular expression");
|
|
1790
|
+
this.nextToken();
|
|
1791
|
+
return { kind: SyntaxKind.RegularExpressionLiteral, text: token.text, pattern: "", flags: "", start: token.start, end: token.end };
|
|
1792
|
+
}
|
|
1793
|
+
i++;
|
|
1794
|
+
const flagsStart = i;
|
|
1795
|
+
while (i < text.length && /[a-z]/i.test(text[i]))
|
|
1796
|
+
i++;
|
|
1797
|
+
const flags = text.slice(flagsStart, i);
|
|
1798
|
+
this.scanner.seek(i);
|
|
1799
|
+
this.tokens = [this.scanner.nextToken()];
|
|
1800
|
+
this.index = 0;
|
|
1801
|
+
return { kind: SyntaxKind.RegularExpressionLiteral, text: text.slice(token.start, i), pattern, flags, start: token.start, end: i };
|
|
1802
|
+
}
|
|
1803
|
+
parseTypeArguments() {
|
|
1804
|
+
this.parseExpected(TokenKind.LessThan);
|
|
1805
|
+
const types = [];
|
|
1806
|
+
while (!this.at(TokenKind.GreaterThan) && !this.at(TokenKind.EndOfFile)) {
|
|
1807
|
+
types.push(this.parseType());
|
|
1808
|
+
if (this.at(TokenKind.Comma))
|
|
1809
|
+
this.nextToken();
|
|
1810
|
+
else
|
|
1811
|
+
break;
|
|
1812
|
+
}
|
|
1813
|
+
this.parseGreaterThan();
|
|
1814
|
+
return types;
|
|
1815
|
+
}
|
|
1816
|
+
// -- types ----------------------------------------------------------------
|
|
1817
|
+
parseType() {
|
|
1818
|
+
const start = this.token.start;
|
|
1819
|
+
const checkType = this.parseUnionType();
|
|
1820
|
+
if (this.at(TokenKind.ExtendsKeyword)) {
|
|
1821
|
+
this.nextToken();
|
|
1822
|
+
const extendsType = this.parseUnionType();
|
|
1823
|
+
this.parseExpected(TokenKind.Question);
|
|
1824
|
+
const trueType = this.parseType();
|
|
1825
|
+
this.parseExpected(TokenKind.Colon);
|
|
1826
|
+
const falseType = this.parseType();
|
|
1827
|
+
return { kind: SyntaxKind.ConditionalType, checkType, extendsType, trueType, falseType, start, end: falseType.end };
|
|
1828
|
+
}
|
|
1829
|
+
return checkType;
|
|
1830
|
+
}
|
|
1831
|
+
parseUnionType() {
|
|
1832
|
+
const start = this.token.start;
|
|
1833
|
+
const leading = this.at(TokenKind.Bar);
|
|
1834
|
+
if (leading)
|
|
1835
|
+
this.nextToken();
|
|
1836
|
+
const first = this.parseIntersectionType();
|
|
1837
|
+
if (!this.at(TokenKind.Bar))
|
|
1838
|
+
return first;
|
|
1839
|
+
const types = [first];
|
|
1840
|
+
while (this.at(TokenKind.Bar)) {
|
|
1841
|
+
this.nextToken();
|
|
1842
|
+
types.push(this.parseIntersectionType());
|
|
1843
|
+
}
|
|
1844
|
+
return { kind: SyntaxKind.UnionType, types, start, end: types[types.length - 1].end };
|
|
1845
|
+
}
|
|
1846
|
+
parseIntersectionType() {
|
|
1847
|
+
const start = this.token.start;
|
|
1848
|
+
const leading = this.at(TokenKind.Ampersand);
|
|
1849
|
+
if (leading)
|
|
1850
|
+
this.nextToken();
|
|
1851
|
+
const first = this.parseTypeOperatorOrHigher();
|
|
1852
|
+
if (!this.at(TokenKind.Ampersand))
|
|
1853
|
+
return first;
|
|
1854
|
+
const types = [first];
|
|
1855
|
+
while (this.at(TokenKind.Ampersand)) {
|
|
1856
|
+
this.nextToken();
|
|
1857
|
+
types.push(this.parseTypeOperatorOrHigher());
|
|
1858
|
+
}
|
|
1859
|
+
return { kind: SyntaxKind.IntersectionType, types, start, end: types[types.length - 1].end };
|
|
1860
|
+
}
|
|
1861
|
+
parseTypeOperatorOrHigher() {
|
|
1862
|
+
const start = this.token.start;
|
|
1863
|
+
if (this.at(TokenKind.KeyOfKeyword)) {
|
|
1864
|
+
this.nextToken();
|
|
1865
|
+
const type = this.parseTypeOperatorOrHigher();
|
|
1866
|
+
return { kind: SyntaxKind.TypeOperator, operator: TypeOperator.KeyOf, type, start, end: type.end };
|
|
1867
|
+
}
|
|
1868
|
+
if (this.at(TokenKind.UniqueKeyword)) {
|
|
1869
|
+
this.nextToken();
|
|
1870
|
+
const type = this.parseTypeOperatorOrHigher();
|
|
1871
|
+
return { kind: SyntaxKind.TypeOperator, operator: TypeOperator.Unique, type, start, end: type.end };
|
|
1872
|
+
}
|
|
1873
|
+
if (this.at(TokenKind.ReadonlyKeyword)) {
|
|
1874
|
+
this.nextToken();
|
|
1875
|
+
const type = this.parseTypeOperatorOrHigher();
|
|
1876
|
+
return { kind: SyntaxKind.TypeOperator, operator: TypeOperator.Readonly, type, start, end: type.end };
|
|
1877
|
+
}
|
|
1878
|
+
if (this.at(TokenKind.InferKeyword)) {
|
|
1879
|
+
this.nextToken();
|
|
1880
|
+
const name = this.parseIdentifier();
|
|
1881
|
+
let constraint;
|
|
1882
|
+
if (this.at(TokenKind.ExtendsKeyword)) {
|
|
1883
|
+
this.nextToken();
|
|
1884
|
+
constraint = this.parseType();
|
|
1885
|
+
}
|
|
1886
|
+
const typeParameter = { kind: SyntaxKind.TypeParameterDeclaration, name, constraint, start: name.start, end: constraint?.end ?? name.end };
|
|
1887
|
+
return { kind: SyntaxKind.InferType, typeParameter, start, end: typeParameter.end };
|
|
1888
|
+
}
|
|
1889
|
+
return this.parsePostfixType();
|
|
1890
|
+
}
|
|
1891
|
+
parsePostfixType() {
|
|
1892
|
+
const start = this.token.start;
|
|
1893
|
+
let type = this.parsePrimaryType();
|
|
1894
|
+
for (;;) {
|
|
1895
|
+
if (this.at(TokenKind.OpenBracket)) {
|
|
1896
|
+
this.nextToken();
|
|
1897
|
+
if (this.at(TokenKind.CloseBracket)) {
|
|
1898
|
+
const close = this.nextToken();
|
|
1899
|
+
type = { kind: SyntaxKind.ArrayType, elementType: type, start, end: close.end };
|
|
1900
|
+
}
|
|
1901
|
+
else {
|
|
1902
|
+
const indexType = this.parseType();
|
|
1903
|
+
const close = this.parseExpected(TokenKind.CloseBracket);
|
|
1904
|
+
type = { kind: SyntaxKind.IndexedAccessType, objectType: type, indexType, start, end: close.end };
|
|
1905
|
+
}
|
|
1906
|
+
continue;
|
|
1907
|
+
}
|
|
1908
|
+
if (this.at(TokenKind.Question) && this.atAhead(1, TokenKind.Colon)) {
|
|
1909
|
+
// Optional tuple member, handled inside tuples only.
|
|
1910
|
+
}
|
|
1911
|
+
return type;
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
parsePrimaryType() {
|
|
1915
|
+
const token = this.token;
|
|
1916
|
+
const start = token.start;
|
|
1917
|
+
switch (token.kind) {
|
|
1918
|
+
case TokenKind.AnyKeyword:
|
|
1919
|
+
this.nextToken();
|
|
1920
|
+
return { kind: SyntaxKind.AnyKeywordType, start, end: token.end };
|
|
1921
|
+
case TokenKind.UnknownKeyword:
|
|
1922
|
+
this.nextToken();
|
|
1923
|
+
return { kind: SyntaxKind.UnknownKeywordType, start, end: token.end };
|
|
1924
|
+
case TokenKind.NumberKeyword:
|
|
1925
|
+
this.nextToken();
|
|
1926
|
+
return { kind: SyntaxKind.NumberKeywordType, start, end: token.end };
|
|
1927
|
+
case TokenKind.BigIntKeyword:
|
|
1928
|
+
this.nextToken();
|
|
1929
|
+
return { kind: SyntaxKind.BigIntKeywordType, start, end: token.end };
|
|
1930
|
+
case TokenKind.StringKeyword:
|
|
1931
|
+
this.nextToken();
|
|
1932
|
+
return { kind: SyntaxKind.StringKeywordType, start, end: token.end };
|
|
1933
|
+
case TokenKind.BooleanKeyword:
|
|
1934
|
+
this.nextToken();
|
|
1935
|
+
return { kind: SyntaxKind.BooleanKeywordType, start, end: token.end };
|
|
1936
|
+
case TokenKind.SymbolKeyword:
|
|
1937
|
+
this.nextToken();
|
|
1938
|
+
return { kind: SyntaxKind.SymbolKeywordType, start, end: token.end };
|
|
1939
|
+
case TokenKind.VoidKeyword:
|
|
1940
|
+
this.nextToken();
|
|
1941
|
+
return { kind: SyntaxKind.VoidKeywordType, start, end: token.end };
|
|
1942
|
+
case TokenKind.UndefinedKeyword:
|
|
1943
|
+
this.nextToken();
|
|
1944
|
+
return { kind: SyntaxKind.UndefinedKeywordType, start, end: token.end };
|
|
1945
|
+
case TokenKind.NullKeyword:
|
|
1946
|
+
this.nextToken();
|
|
1947
|
+
return { kind: SyntaxKind.NullKeywordType, start, end: token.end };
|
|
1948
|
+
case TokenKind.NeverKeyword:
|
|
1949
|
+
this.nextToken();
|
|
1950
|
+
return { kind: SyntaxKind.NeverKeywordType, start, end: token.end };
|
|
1951
|
+
case TokenKind.ObjectKeyword:
|
|
1952
|
+
this.nextToken();
|
|
1953
|
+
return { kind: SyntaxKind.ObjectKeywordType, start, end: token.end };
|
|
1954
|
+
case TokenKind.ThisKeyword:
|
|
1955
|
+
this.nextToken();
|
|
1956
|
+
return { kind: SyntaxKind.ThisKeywordType, start, end: token.end };
|
|
1957
|
+
case TokenKind.TypeOfKeyword: {
|
|
1958
|
+
this.nextToken();
|
|
1959
|
+
const exprName = this.parseEntityName();
|
|
1960
|
+
const typeArguments = this.at(TokenKind.LessThan) ? this.parseTypeArguments() : [];
|
|
1961
|
+
return { kind: SyntaxKind.TypeQuery, exprName, typeArguments, start, end: this.token.start };
|
|
1962
|
+
}
|
|
1963
|
+
case TokenKind.OpenBrace:
|
|
1964
|
+
return this.parseTypeLiteral();
|
|
1965
|
+
case TokenKind.OpenBracket:
|
|
1966
|
+
return this.parseTupleType();
|
|
1967
|
+
case TokenKind.OpenParen: {
|
|
1968
|
+
const closeIndex = this.findMatchingParen(0);
|
|
1969
|
+
if (closeIndex >= 0 && this.lookAhead(closeIndex + 1).kind === TokenKind.EqualsGreaterThan) {
|
|
1970
|
+
return this.parseFunctionType();
|
|
1971
|
+
}
|
|
1972
|
+
this.nextToken();
|
|
1973
|
+
const type = this.parseType();
|
|
1974
|
+
const close = this.parseExpected(TokenKind.CloseParen);
|
|
1975
|
+
return { kind: SyntaxKind.ParenthesizedType, type, start, end: close.end };
|
|
1976
|
+
}
|
|
1977
|
+
case TokenKind.LessThan:
|
|
1978
|
+
return this.parseFunctionType();
|
|
1979
|
+
case TokenKind.NewKeyword: {
|
|
1980
|
+
this.nextToken();
|
|
1981
|
+
return this.parseFunctionType(start);
|
|
1982
|
+
}
|
|
1983
|
+
case TokenKind.StringLiteral: {
|
|
1984
|
+
this.nextToken();
|
|
1985
|
+
const literal = this.stringLiteralFromToken(token);
|
|
1986
|
+
return { kind: SyntaxKind.LiteralType, literal, start, end: token.end };
|
|
1987
|
+
}
|
|
1988
|
+
case TokenKind.NumericLiteral: {
|
|
1989
|
+
this.nextToken();
|
|
1990
|
+
const literal = { kind: SyntaxKind.NumericLiteral, text: token.text, value: Number(token.value ?? 0), start, end: token.end };
|
|
1991
|
+
return { kind: SyntaxKind.LiteralType, literal, start, end: token.end };
|
|
1992
|
+
}
|
|
1993
|
+
case TokenKind.TrueKeyword: {
|
|
1994
|
+
this.nextToken();
|
|
1995
|
+
const literal = { kind: SyntaxKind.TrueKeyword, value: true, start, end: token.end };
|
|
1996
|
+
return { kind: SyntaxKind.LiteralType, literal, start, end: token.end };
|
|
1997
|
+
}
|
|
1998
|
+
case TokenKind.FalseKeyword: {
|
|
1999
|
+
this.nextToken();
|
|
2000
|
+
const literal = { kind: SyntaxKind.FalseKeyword, value: false, start, end: token.end };
|
|
2001
|
+
return { kind: SyntaxKind.LiteralType, literal, start, end: token.end };
|
|
2002
|
+
}
|
|
2003
|
+
case TokenKind.Minus: {
|
|
2004
|
+
this.nextToken();
|
|
2005
|
+
const num = this.parseExpected(TokenKind.NumericLiteral);
|
|
2006
|
+
const literal = { kind: SyntaxKind.NumericLiteral, text: `-${num.text}`, value: -Number(num.value ?? 0), start, end: num.end };
|
|
2007
|
+
return { kind: SyntaxKind.LiteralType, literal, start, end: num.end };
|
|
2008
|
+
}
|
|
2009
|
+
case TokenKind.ImportKeyword: {
|
|
2010
|
+
// import("...").T - parse loosely and degrade to `any`.
|
|
2011
|
+
this.nextToken();
|
|
2012
|
+
if (this.at(TokenKind.OpenParen)) {
|
|
2013
|
+
this.nextToken();
|
|
2014
|
+
if (this.at(TokenKind.StringLiteral))
|
|
2015
|
+
this.nextToken();
|
|
2016
|
+
this.parseExpected(TokenKind.CloseParen);
|
|
2017
|
+
}
|
|
2018
|
+
while (this.at(TokenKind.Dot)) {
|
|
2019
|
+
this.nextToken();
|
|
2020
|
+
this.parseIdentifierName();
|
|
2021
|
+
}
|
|
2022
|
+
const typeArguments = this.at(TokenKind.LessThan) ? this.parseTypeArguments() : [];
|
|
2023
|
+
return { kind: SyntaxKind.AnyKeywordType, start, end: this.token.start, typeArguments };
|
|
2024
|
+
}
|
|
2025
|
+
default:
|
|
2026
|
+
break;
|
|
2027
|
+
}
|
|
2028
|
+
if (this.isIdentifierLike(token)) {
|
|
2029
|
+
const typeName = this.parseEntityName();
|
|
2030
|
+
const typeArguments = this.at(TokenKind.LessThan) ? this.parseTypeArguments() : [];
|
|
2031
|
+
return { kind: SyntaxKind.TypeReference, typeName, typeArguments, start, end: this.previousTypeEnd(typeArguments, typeName.end) };
|
|
2032
|
+
}
|
|
2033
|
+
this.error(DiagnosticCode.InvalidTypeSyntax, `Expected a type but found '${token.text || token.kind}'`);
|
|
2034
|
+
if (token.kind !== TokenKind.EndOfFile)
|
|
2035
|
+
this.nextToken();
|
|
2036
|
+
return { kind: SyntaxKind.AnyKeywordType, start, end: token.end };
|
|
2037
|
+
}
|
|
2038
|
+
previousTypeEnd(typeArguments, fallback) {
|
|
2039
|
+
return typeArguments[typeArguments.length - 1]?.end ?? fallback;
|
|
2040
|
+
}
|
|
2041
|
+
parseEntityName() {
|
|
2042
|
+
let left = this.parseIdentifierName();
|
|
2043
|
+
while (this.at(TokenKind.Dot)) {
|
|
2044
|
+
this.nextToken();
|
|
2045
|
+
const right = this.parseIdentifierName();
|
|
2046
|
+
left = { kind: SyntaxKind.QualifiedName, left, right, start: left.start, end: right.end };
|
|
2047
|
+
}
|
|
2048
|
+
return left;
|
|
2049
|
+
}
|
|
2050
|
+
parseFunctionType(explicitStart) {
|
|
2051
|
+
const start = explicitStart ?? this.token.start;
|
|
2052
|
+
const typeParameters = this.at(TokenKind.LessThan) ? this.parseTypeParameters() : [];
|
|
2053
|
+
const parameters = this.parseParameters();
|
|
2054
|
+
this.parseExpected(TokenKind.EqualsGreaterThan);
|
|
2055
|
+
const returnType = this.parseReturnType();
|
|
2056
|
+
return { kind: SyntaxKind.FunctionType, typeParameters, parameters, returnType, start, end: returnType.end };
|
|
2057
|
+
}
|
|
2058
|
+
parseTupleType() {
|
|
2059
|
+
const open = this.parseExpected(TokenKind.OpenBracket);
|
|
2060
|
+
const elements = [];
|
|
2061
|
+
while (!this.at(TokenKind.CloseBracket) && !this.at(TokenKind.EndOfFile)) {
|
|
2062
|
+
let element;
|
|
2063
|
+
if (this.at(TokenKind.DotDotDot)) {
|
|
2064
|
+
const restStart = this.nextToken().start;
|
|
2065
|
+
const type = this.parseType();
|
|
2066
|
+
element = { kind: SyntaxKind.RestType, type, start: restStart, end: type.end };
|
|
2067
|
+
}
|
|
2068
|
+
else {
|
|
2069
|
+
element = this.parseType();
|
|
2070
|
+
}
|
|
2071
|
+
if (this.at(TokenKind.Question)) {
|
|
2072
|
+
const end = this.nextToken().end;
|
|
2073
|
+
element = { kind: SyntaxKind.OptionalType, type: element, start: element.start, end };
|
|
2074
|
+
}
|
|
2075
|
+
elements.push(element);
|
|
2076
|
+
if (this.at(TokenKind.Comma))
|
|
2077
|
+
this.nextToken();
|
|
2078
|
+
else
|
|
2079
|
+
break;
|
|
2080
|
+
}
|
|
2081
|
+
const close = this.parseExpected(TokenKind.CloseBracket);
|
|
2082
|
+
return { kind: SyntaxKind.TupleType, elements, start: open.start, end: close.end };
|
|
2083
|
+
}
|
|
2084
|
+
parseTypeLiteral() {
|
|
2085
|
+
const start = this.token.start;
|
|
2086
|
+
// Detect mapped type: { [K in T]: U }
|
|
2087
|
+
let cursor = 0;
|
|
2088
|
+
if (this.atAhead(1, TokenKind.OpenBracket)) {
|
|
2089
|
+
cursor = 1;
|
|
2090
|
+
if (this.atAhead(2, TokenKind.Identifier)) {
|
|
2091
|
+
let i = 3;
|
|
2092
|
+
if (this.lookAhead(i).kind === TokenKind.InKeyword) {
|
|
2093
|
+
const open = this.parseExpected(TokenKind.OpenBrace);
|
|
2094
|
+
this.parseExpected(TokenKind.OpenBracket);
|
|
2095
|
+
const name = this.parseIdentifier();
|
|
2096
|
+
this.parseExpected(TokenKind.InKeyword);
|
|
2097
|
+
let constraint = this.parseType();
|
|
2098
|
+
this.parseExpected(TokenKind.CloseBracket);
|
|
2099
|
+
let questionToken = false;
|
|
2100
|
+
if (this.at(TokenKind.Question)) {
|
|
2101
|
+
this.nextToken();
|
|
2102
|
+
questionToken = true;
|
|
2103
|
+
}
|
|
2104
|
+
else if (this.at(TokenKind.Plus)) {
|
|
2105
|
+
this.nextToken();
|
|
2106
|
+
this.nextToken();
|
|
2107
|
+
questionToken = "+";
|
|
2108
|
+
}
|
|
2109
|
+
else if (this.at(TokenKind.Minus)) {
|
|
2110
|
+
this.nextToken();
|
|
2111
|
+
this.nextToken();
|
|
2112
|
+
questionToken = "-";
|
|
2113
|
+
}
|
|
2114
|
+
let type;
|
|
2115
|
+
if (this.at(TokenKind.Colon)) {
|
|
2116
|
+
this.nextToken();
|
|
2117
|
+
type = this.parseType();
|
|
2118
|
+
}
|
|
2119
|
+
this.parseSemicolonOrComma();
|
|
2120
|
+
const close = this.parseExpected(TokenKind.CloseBrace);
|
|
2121
|
+
const typeParameter = { kind: SyntaxKind.TypeParameterDeclaration, name, constraint, start: name.start, end: constraint.end };
|
|
2122
|
+
return { kind: SyntaxKind.MappedType, readonlyToken: false, typeParameter, questionToken, type, start: open.start, end: close.end };
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
void cursor;
|
|
2127
|
+
const members = this.parseTypeMembers();
|
|
2128
|
+
return { kind: SyntaxKind.TypeLiteral, members, start, end: this.token.start };
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
function expressionToPropertyName(expr) {
|
|
2132
|
+
switch (expr.kind) {
|
|
2133
|
+
case SyntaxKind.StringLiteral:
|
|
2134
|
+
return expr;
|
|
2135
|
+
case SyntaxKind.NumericLiteral:
|
|
2136
|
+
return expr;
|
|
2137
|
+
case SyntaxKind.Identifier:
|
|
2138
|
+
return expr;
|
|
2139
|
+
default:
|
|
2140
|
+
return undefined;
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
function propertyNameText(name) {
|
|
2144
|
+
switch (name.kind) {
|
|
2145
|
+
case SyntaxKind.Identifier:
|
|
2146
|
+
case SyntaxKind.PrivateIdentifier:
|
|
2147
|
+
return name.text;
|
|
2148
|
+
case SyntaxKind.StringLiteral:
|
|
2149
|
+
return name.value;
|
|
2150
|
+
case SyntaxKind.NumericLiteral:
|
|
2151
|
+
return name.text;
|
|
2152
|
+
default:
|
|
2153
|
+
return "";
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
function isAssignmentTarget(expr) {
|
|
2157
|
+
return (expr.kind === SyntaxKind.Identifier ||
|
|
2158
|
+
expr.kind === SyntaxKind.PropertyAccessExpression ||
|
|
2159
|
+
expr.kind === SyntaxKind.ElementAccessExpression ||
|
|
2160
|
+
expr.kind === SyntaxKind.ParenthesizedExpression);
|
|
2161
|
+
}
|
|
2162
|
+
function assignmentOperator(kind) {
|
|
2163
|
+
switch (kind) {
|
|
2164
|
+
case TokenKind.Equals:
|
|
2165
|
+
return AssignmentOperator.Assign;
|
|
2166
|
+
case TokenKind.PlusEquals:
|
|
2167
|
+
return AssignmentOperator.AddAssign;
|
|
2168
|
+
case TokenKind.MinusEquals:
|
|
2169
|
+
return AssignmentOperator.SubtractAssign;
|
|
2170
|
+
case TokenKind.AsteriskEquals:
|
|
2171
|
+
return AssignmentOperator.MultiplyAssign;
|
|
2172
|
+
case TokenKind.SlashEquals:
|
|
2173
|
+
return AssignmentOperator.DivideAssign;
|
|
2174
|
+
case TokenKind.PercentEquals:
|
|
2175
|
+
return AssignmentOperator.RemainderAssign;
|
|
2176
|
+
case TokenKind.AsteriskAsteriskEquals:
|
|
2177
|
+
return AssignmentOperator.ExponentAssign;
|
|
2178
|
+
case TokenKind.LessThanLessThanEquals:
|
|
2179
|
+
return AssignmentOperator.LessThanLessThanAssign;
|
|
2180
|
+
case TokenKind.GreaterThanGreaterThanEquals:
|
|
2181
|
+
return AssignmentOperator.GreaterThanGreaterThanAssign;
|
|
2182
|
+
case TokenKind.GreaterThanGreaterThanGreaterThanEquals:
|
|
2183
|
+
return AssignmentOperator.GreaterThanGreaterThanGreaterThanAssign;
|
|
2184
|
+
case TokenKind.AmpersandEquals:
|
|
2185
|
+
return AssignmentOperator.AmpersandAssign;
|
|
2186
|
+
case TokenKind.BarEquals:
|
|
2187
|
+
return AssignmentOperator.BarAssign;
|
|
2188
|
+
case TokenKind.CaretEquals:
|
|
2189
|
+
return AssignmentOperator.CaretAssign;
|
|
2190
|
+
case TokenKind.AmpersandAmpersandEquals:
|
|
2191
|
+
return AssignmentOperator.AmpersandAmpersandAssign;
|
|
2192
|
+
case TokenKind.BarBarEquals:
|
|
2193
|
+
return AssignmentOperator.BarBarAssign;
|
|
2194
|
+
case TokenKind.QuestionQuestionEquals:
|
|
2195
|
+
return AssignmentOperator.QuestionQuestionAssign;
|
|
2196
|
+
default:
|
|
2197
|
+
return undefined;
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
/** `as` / `satisfies` share the relational precedence level. */
|
|
2201
|
+
const AS_PRECEDENCE = 8;
|
|
2202
|
+
function binaryOperator(kind) {
|
|
2203
|
+
switch (kind) {
|
|
2204
|
+
case TokenKind.Plus:
|
|
2205
|
+
return BinaryOperator.Add;
|
|
2206
|
+
case TokenKind.Minus:
|
|
2207
|
+
return BinaryOperator.Subtract;
|
|
2208
|
+
case TokenKind.Asterisk:
|
|
2209
|
+
return BinaryOperator.Multiply;
|
|
2210
|
+
case TokenKind.Slash:
|
|
2211
|
+
return BinaryOperator.Divide;
|
|
2212
|
+
case TokenKind.Percent:
|
|
2213
|
+
return BinaryOperator.Remainder;
|
|
2214
|
+
case TokenKind.AsteriskAsterisk:
|
|
2215
|
+
return BinaryOperator.Exponent;
|
|
2216
|
+
case TokenKind.LessThan:
|
|
2217
|
+
return BinaryOperator.LessThan;
|
|
2218
|
+
case TokenKind.LessThanEquals:
|
|
2219
|
+
return BinaryOperator.LessThanEquals;
|
|
2220
|
+
case TokenKind.GreaterThan:
|
|
2221
|
+
return BinaryOperator.GreaterThan;
|
|
2222
|
+
case TokenKind.GreaterThanEquals:
|
|
2223
|
+
return BinaryOperator.GreaterThanEquals;
|
|
2224
|
+
case TokenKind.EqualsEquals:
|
|
2225
|
+
return BinaryOperator.EqualsEquals;
|
|
2226
|
+
case TokenKind.ExclamationEquals:
|
|
2227
|
+
return BinaryOperator.ExclamationEquals;
|
|
2228
|
+
case TokenKind.EqualsEqualsEquals:
|
|
2229
|
+
return BinaryOperator.EqualsEqualsEquals;
|
|
2230
|
+
case TokenKind.ExclamationEqualsEquals:
|
|
2231
|
+
return BinaryOperator.ExclamationEqualsEquals;
|
|
2232
|
+
case TokenKind.AmpersandAmpersand:
|
|
2233
|
+
return BinaryOperator.AmpersandAmpersand;
|
|
2234
|
+
case TokenKind.BarBar:
|
|
2235
|
+
return BinaryOperator.BarBar;
|
|
2236
|
+
case TokenKind.QuestionQuestion:
|
|
2237
|
+
return BinaryOperator.QuestionQuestion;
|
|
2238
|
+
case TokenKind.Ampersand:
|
|
2239
|
+
return BinaryOperator.Ampersand;
|
|
2240
|
+
case TokenKind.Bar:
|
|
2241
|
+
return BinaryOperator.Bar;
|
|
2242
|
+
case TokenKind.Caret:
|
|
2243
|
+
return BinaryOperator.Caret;
|
|
2244
|
+
case TokenKind.LessThanLessThan:
|
|
2245
|
+
return BinaryOperator.LessThanLessThan;
|
|
2246
|
+
case TokenKind.GreaterThanGreaterThan:
|
|
2247
|
+
return BinaryOperator.GreaterThanGreaterThan;
|
|
2248
|
+
case TokenKind.GreaterThanGreaterThanGreaterThan:
|
|
2249
|
+
return BinaryOperator.GreaterThanGreaterThanGreaterThan;
|
|
2250
|
+
case TokenKind.InKeyword:
|
|
2251
|
+
return BinaryOperator.In;
|
|
2252
|
+
case TokenKind.InstanceOfKeyword:
|
|
2253
|
+
return BinaryOperator.InstanceOf;
|
|
2254
|
+
default:
|
|
2255
|
+
return undefined;
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
function binaryPrecedence(op) {
|
|
2259
|
+
switch (op) {
|
|
2260
|
+
case BinaryOperator.QuestionQuestion:
|
|
2261
|
+
return 1;
|
|
2262
|
+
case BinaryOperator.BarBar:
|
|
2263
|
+
return 2;
|
|
2264
|
+
case BinaryOperator.AmpersandAmpersand:
|
|
2265
|
+
return 3;
|
|
2266
|
+
case BinaryOperator.Bar:
|
|
2267
|
+
return 4;
|
|
2268
|
+
case BinaryOperator.Caret:
|
|
2269
|
+
return 5;
|
|
2270
|
+
case BinaryOperator.Ampersand:
|
|
2271
|
+
return 6;
|
|
2272
|
+
case BinaryOperator.EqualsEquals:
|
|
2273
|
+
case BinaryOperator.ExclamationEquals:
|
|
2274
|
+
case BinaryOperator.EqualsEqualsEquals:
|
|
2275
|
+
case BinaryOperator.ExclamationEqualsEquals:
|
|
2276
|
+
return 7;
|
|
2277
|
+
case BinaryOperator.LessThan:
|
|
2278
|
+
case BinaryOperator.LessThanEquals:
|
|
2279
|
+
case BinaryOperator.GreaterThan:
|
|
2280
|
+
case BinaryOperator.GreaterThanEquals:
|
|
2281
|
+
case BinaryOperator.In:
|
|
2282
|
+
case BinaryOperator.InstanceOf:
|
|
2283
|
+
return 8;
|
|
2284
|
+
case BinaryOperator.LessThanLessThan:
|
|
2285
|
+
case BinaryOperator.GreaterThanGreaterThan:
|
|
2286
|
+
case BinaryOperator.GreaterThanGreaterThanGreaterThan:
|
|
2287
|
+
return 9;
|
|
2288
|
+
case BinaryOperator.Add:
|
|
2289
|
+
case BinaryOperator.Subtract:
|
|
2290
|
+
return 10;
|
|
2291
|
+
case BinaryOperator.Multiply:
|
|
2292
|
+
case BinaryOperator.Divide:
|
|
2293
|
+
case BinaryOperator.Remainder:
|
|
2294
|
+
return 11;
|
|
2295
|
+
case BinaryOperator.Exponent:
|
|
2296
|
+
return 12;
|
|
2297
|
+
default:
|
|
2298
|
+
return -1;
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
function prefixUnaryOperator(kind) {
|
|
2302
|
+
switch (kind) {
|
|
2303
|
+
case TokenKind.Plus:
|
|
2304
|
+
return PrefixUnaryOperator.Plus;
|
|
2305
|
+
case TokenKind.Minus:
|
|
2306
|
+
return PrefixUnaryOperator.Minus;
|
|
2307
|
+
case TokenKind.Tilde:
|
|
2308
|
+
return PrefixUnaryOperator.Tilde;
|
|
2309
|
+
case TokenKind.Exclamation:
|
|
2310
|
+
return PrefixUnaryOperator.Exclamation;
|
|
2311
|
+
case TokenKind.TypeOfKeyword:
|
|
2312
|
+
return PrefixUnaryOperator.TypeOf;
|
|
2313
|
+
case TokenKind.VoidKeyword:
|
|
2314
|
+
return PrefixUnaryOperator.Void;
|
|
2315
|
+
case TokenKind.DeleteKeyword:
|
|
2316
|
+
return PrefixUnaryOperator.Delete;
|
|
2317
|
+
case TokenKind.PlusPlus:
|
|
2318
|
+
return PrefixUnaryOperator.PlusPlus;
|
|
2319
|
+
case TokenKind.MinusMinus:
|
|
2320
|
+
return PrefixUnaryOperator.MinusMinus;
|
|
2321
|
+
case TokenKind.AwaitKeyword:
|
|
2322
|
+
return PrefixUnaryOperator.Await;
|
|
2323
|
+
default:
|
|
2324
|
+
return undefined;
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
/** Thrown internally to unwind a speculative parse; never escapes the parser. */
|
|
2328
|
+
class SpeculationError extends Error {
|
|
2329
|
+
constructor() {
|
|
2330
|
+
super("speculation failed");
|
|
2331
|
+
this.name = "SpeculationError";
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
//# sourceMappingURL=parser.js.map
|