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