xbintsc 0.2.4 → 0.2.5

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.
Files changed (73) hide show
  1. package/dist/src/ast/declarations.d.ts +20 -1
  2. package/dist/src/ast/kinds.d.ts +5 -1
  3. package/dist/src/ast/kinds.js +4 -0
  4. package/dist/src/ast/kinds.js.map +1 -1
  5. package/dist/src/ast/statements.d.ts +2 -1
  6. package/dist/src/binder/binder.js +44 -9
  7. package/dist/src/binder/binder.js.map +1 -1
  8. package/dist/src/cli/main.js +2 -4
  9. package/dist/src/cli/main.js.map +1 -1
  10. package/dist/src/codegen/generator/expressions.js +25 -0
  11. package/dist/src/codegen/generator/expressions.js.map +1 -1
  12. package/dist/src/codegen/generator/module.js +16 -5
  13. package/dist/src/codegen/generator/module.js.map +1 -1
  14. package/dist/src/codegen/generator/state.js +1 -0
  15. package/dist/src/codegen/generator/state.js.map +1 -1
  16. package/dist/src/codegen/generator/statements.d.ts +4 -1
  17. package/dist/src/codegen/generator/statements.js +121 -7
  18. package/dist/src/codegen/generator/statements.js.map +1 -1
  19. package/dist/src/codegen/generator/tables.js +1 -0
  20. package/dist/src/codegen/generator/tables.js.map +1 -1
  21. package/dist/src/driver/compiler.d.ts +5 -0
  22. package/dist/src/driver/compiler.js +26 -0
  23. package/dist/src/driver/compiler.js.map +1 -1
  24. package/dist/src/driver/modules.js +29 -4
  25. package/dist/src/driver/modules.js.map +1 -1
  26. package/dist/src/lexer/token.d.ts +10 -0
  27. package/dist/src/lexer/token.js +69 -0
  28. package/dist/src/lexer/token.js.map +1 -1
  29. package/dist/src/parser/context.js +4 -16
  30. package/dist/src/parser/context.js.map +1 -1
  31. package/dist/src/parser/declarations.d.ts +2 -0
  32. package/dist/src/parser/declarations.js +136 -24
  33. package/dist/src/parser/declarations.js.map +1 -1
  34. package/dist/src/parser/expressions.js +19 -2
  35. package/dist/src/parser/expressions.js.map +1 -1
  36. package/dist/src/parser/helpers.js +2 -0
  37. package/dist/src/parser/helpers.js.map +1 -1
  38. package/dist/src/parser/modules.js +7 -2
  39. package/dist/src/parser/modules.js.map +1 -1
  40. package/dist/src/parser/statements.js +28 -3
  41. package/dist/src/parser/statements.js.map +1 -1
  42. package/dist/src/parser/types.js +5 -0
  43. package/dist/src/parser/types.js.map +1 -1
  44. package/dist/tests/codegen/llvm.test.js +16 -0
  45. package/dist/tests/codegen/llvm.test.js.map +1 -1
  46. package/dist/tests/driver/compiler.test.js +13 -1
  47. package/dist/tests/driver/compiler.test.js.map +1 -1
  48. package/dist/tests/e2e/compile.test.js +21 -0
  49. package/dist/tests/e2e/compile.test.js.map +1 -1
  50. package/dist/tests/parser/parser.test.js +53 -0
  51. package/dist/tests/parser/parser.test.js.map +1 -1
  52. package/package.json +1 -1
  53. package/runtime/xt_stdlib2.c +11 -0
  54. package/src/ast/declarations.ts +26 -1
  55. package/src/ast/kinds.ts +4 -0
  56. package/src/ast/statements.ts +2 -1
  57. package/src/binder/binder.ts +46 -9
  58. package/src/cli/main.ts +2 -3
  59. package/src/codegen/generator/expressions.ts +25 -0
  60. package/src/codegen/generator/module.ts +19 -6
  61. package/src/codegen/generator/state.ts +1 -0
  62. package/src/codegen/generator/statements.ts +118 -5
  63. package/src/codegen/generator/tables.ts +1 -0
  64. package/src/driver/compiler.ts +29 -0
  65. package/src/driver/modules.ts +28 -3
  66. package/src/lexer/token.ts +73 -0
  67. package/src/parser/context.ts +4 -15
  68. package/src/parser/declarations.ts +130 -22
  69. package/src/parser/expressions.ts +19 -3
  70. package/src/parser/helpers.ts +2 -0
  71. package/src/parser/modules.ts +7 -2
  72. package/src/parser/statements.ts +27 -3
  73. package/src/parser/types.ts +5 -0
@@ -10,7 +10,7 @@
10
10
  import { DiagnosticBag, DiagnosticCode } from "../diagnostics/diagnostic.js";
11
11
  import type { SourceFile } from "../diagnostics/source.js";
12
12
  import { Scanner } from "../lexer/scanner.js";
13
- import { Token, TokenKind } from "../lexer/token.js";
13
+ import { isIdentifierNameToken, isKeywordKind, Token, TokenKind } from "../lexer/token.js";
14
14
  import { SyntaxKind } from "../ast/kinds.js";
15
15
  import type { Identifier, StringLiteral } from "../ast/nodes.js";
16
16
  import { SpeculationError } from "./speculation.js";
@@ -121,24 +121,13 @@ export class ParserContext {
121
121
  }
122
122
 
123
123
  isIdentifierLike(token: Token): boolean {
124
- if (token.kind === TokenKind.Identifier) return true;
125
- switch (token.kind) {
126
- case TokenKind.AsKeyword:
127
- case TokenKind.SatisfiesKeyword:
128
- case TokenKind.FromKeyword:
129
- case TokenKind.TypeKeyword:
130
- case TokenKind.GetKeyword:
131
- case TokenKind.SetKeyword:
132
- case TokenKind.AsyncKeyword:
133
- return true;
134
- default:
135
- return false;
136
- }
124
+ return isIdentifierNameToken(token.kind);
137
125
  }
138
126
 
139
127
  parseIdentifier(allowKeywords = false): Identifier {
140
128
  const token = this.token;
141
- if (token.kind === TokenKind.Identifier || (allowKeywords && this.isIdentifierLike(token))) {
129
+ const ok = allowKeywords ? isKeywordKind(token.kind) : isIdentifierNameToken(token.kind);
130
+ if (token.kind === TokenKind.Identifier || ok) {
142
131
  this.nextToken();
143
132
  return { kind: SyntaxKind.Identifier, text: token.text, start: token.start, end: token.end };
144
133
  }
@@ -37,6 +37,7 @@ import {
37
37
  type TypeParameterDeclaration,
38
38
  } from "../ast/nodes.js";
39
39
  import { expressionToPropertyName } from "./helpers.js";
40
+ import { SpeculationError } from "./speculation.js";
40
41
  import type { Parser } from "./parser.js";
41
42
 
42
43
  export interface DeclarationMethods {
@@ -44,6 +45,8 @@ export interface DeclarationMethods {
44
45
  parseTypeParameters(this: Parser): TypeParameterDeclaration[];
45
46
  parseGreaterThan(this: Parser): void;
46
47
  parseParameters(this: Parser): Parameter[];
48
+ parseBindingName(this: Parser): import("../ast/declarations.js").BindingName;
49
+ parseBindingElement(this: Parser, allowPropertyName: boolean): import("../ast/declarations.js").BindingElement;
47
50
  parseReturnType(this: Parser): TypeNode;
48
51
  parseClassDeclaration(this: Parser, modifiers: Modifier[]): ClassDeclaration;
49
52
  parseHeritageClauses(this: Parser): HeritageClause[];
@@ -153,7 +156,13 @@ export const declarationMethods: DeclarationMethods = {
153
156
  this.nextToken();
154
157
  dotDotDotToken = true;
155
158
  }
156
- const name = this.parseIdentifier();
159
+ let name: import("../ast/declarations.js").BindingName;
160
+ if (this.at(TokenKind.ThisKeyword)) {
161
+ const thisToken = this.nextToken();
162
+ name = { kind: SyntaxKind.Identifier, text: "this", start: thisToken.start, end: thisToken.end };
163
+ } else {
164
+ name = this.parseBindingName();
165
+ }
157
166
  let questionToken = false;
158
167
  if (this.at(TokenKind.Question)) {
159
168
  this.nextToken();
@@ -177,6 +186,76 @@ export const declarationMethods: DeclarationMethods = {
177
186
  return params;
178
187
  },
179
188
 
189
+ parseBindingName(this: Parser): import("../ast/declarations.js").BindingName {
190
+ if (this.at(TokenKind.OpenBracket)) {
191
+ const open = this.nextToken();
192
+ const elements: (import("../ast/declarations.js").BindingElement | undefined)[] = [];
193
+ while (!this.at(TokenKind.CloseBracket) && !this.at(TokenKind.EndOfFile)) {
194
+ if (this.at(TokenKind.Comma)) {
195
+ elements.push(undefined);
196
+ this.nextToken();
197
+ continue;
198
+ }
199
+ elements.push(this.parseBindingElement(false));
200
+ if (this.at(TokenKind.Comma)) this.nextToken();
201
+ else break;
202
+ }
203
+ const close = this.parseExpected(TokenKind.CloseBracket);
204
+ return { kind: SyntaxKind.ArrayBindingPattern, elements, start: open.start, end: close.end };
205
+ }
206
+ if (this.at(TokenKind.OpenBrace)) {
207
+ const open = this.nextToken();
208
+ const elements: import("../ast/declarations.js").BindingElement[] = [];
209
+ while (!this.at(TokenKind.CloseBrace) && !this.at(TokenKind.EndOfFile)) {
210
+ elements.push(this.parseBindingElement(true));
211
+ if (this.at(TokenKind.Comma)) this.nextToken();
212
+ else break;
213
+ }
214
+ const close = this.parseExpected(TokenKind.CloseBrace);
215
+ return { kind: SyntaxKind.ObjectBindingPattern, elements, start: open.start, end: close.end };
216
+ }
217
+ return this.parseIdentifier();
218
+ },
219
+
220
+ parseBindingElement(this: Parser, allowPropertyName: boolean): import("../ast/declarations.js").BindingElement {
221
+ const start = this.token.start;
222
+ let dotDotDotToken = false;
223
+ if (this.at(TokenKind.DotDotDot)) {
224
+ this.nextToken();
225
+ dotDotDotToken = true;
226
+ }
227
+ let propertyName: PropertyName | undefined;
228
+ let name: import("../ast/declarations.js").BindingName;
229
+ if (allowPropertyName && this.at(TokenKind.OpenBracket)) {
230
+ // Computed property key: `{ [expr]: target }`.
231
+ this.nextToken();
232
+ const expr = this.parseAssignmentExpression();
233
+ this.parseExpected(TokenKind.CloseBracket);
234
+ propertyName = expressionToPropertyName(expr) ?? { kind: SyntaxKind.Identifier, text: "<computed>", start: expr.start, end: expr.end };
235
+ this.parseExpected(TokenKind.Colon);
236
+ name = this.parseBindingName();
237
+ } else if (allowPropertyName && !this.at(TokenKind.OpenBrace) && !this.at(TokenKind.OpenBracket)) {
238
+ const first = this.parseIdentifierName();
239
+ if (this.at(TokenKind.Colon)) {
240
+ this.nextToken();
241
+ propertyName = first;
242
+ name = this.parseBindingName();
243
+ } else {
244
+ name = first;
245
+ }
246
+ } else {
247
+ // Array element, or a nested pattern used as an object value.
248
+ name = this.parseBindingName();
249
+ }
250
+ let initializer: Expression | undefined;
251
+ if (this.at(TokenKind.Equals)) {
252
+ this.nextToken();
253
+ initializer = this.parseAssignmentExpression();
254
+ }
255
+ const end = initializer?.end ?? (name.kind === SyntaxKind.Identifier ? name.end : name.end);
256
+ return { kind: SyntaxKind.BindingElement, name, propertyName, dotDotDotToken, initializer, start, end };
257
+ },
258
+
180
259
  parseReturnType(this: Parser): TypeNode {
181
260
  if (this.at(TokenKind.AssertsKeyword)) {
182
261
  const start = this.nextToken().start;
@@ -249,6 +328,42 @@ export const declarationMethods: DeclarationMethods = {
249
328
  this.nextToken();
250
329
  return undefined;
251
330
  }
331
+ if (
332
+ (this.at(TokenKind.GetKeyword) || this.at(TokenKind.SetKeyword)) &&
333
+ !this.atAhead(1, TokenKind.OpenParen) &&
334
+ !this.atAhead(1, TokenKind.Colon) &&
335
+ !this.atAhead(1, TokenKind.Equals) &&
336
+ !this.atAhead(1, TokenKind.Question) &&
337
+ !this.atAhead(1, TokenKind.LessThan) &&
338
+ !this.atAhead(1, TokenKind.Semicolon)
339
+ ) {
340
+ const accessorToken = this.nextToken();
341
+ const name = this.parsePropertyName();
342
+ const parameters = this.parseParameters();
343
+ let returnType: TypeNode | undefined;
344
+ if (this.at(TokenKind.Colon)) {
345
+ this.nextToken();
346
+ returnType = this.parseReturnType();
347
+ }
348
+ let body: Block | undefined;
349
+ if (this.at(TokenKind.OpenBrace)) body = this.parseBlock();
350
+ else this.parseSemicolon();
351
+ const accessor: MethodDeclaration = {
352
+ kind: SyntaxKind.MethodDeclaration,
353
+ name,
354
+ modifiers,
355
+ typeParameters: [],
356
+ parameters,
357
+ returnType,
358
+ body,
359
+ flags: NodeFlags.None,
360
+ optional: false,
361
+ accessor: accessorToken.kind === TokenKind.GetKeyword ? "get" : "set",
362
+ start,
363
+ end: body?.end ?? returnType?.end ?? name.end,
364
+ };
365
+ return accessor;
366
+ }
252
367
  if (this.at(TokenKind.ConstructorKeyword)) {
253
368
  const name = this.nextToken();
254
369
  const parameters = this.parseParameters();
@@ -439,34 +554,27 @@ export const declarationMethods: DeclarationMethods = {
439
554
  const name: Identifier = { kind: SyntaxKind.Identifier, text: "__call", start, end: start };
440
555
  return { kind: SyntaxKind.MethodSignature, name, questionToken: false, typeParameters, parameters, returnType, start, end: returnType?.end ?? start };
441
556
  }
442
- if (this.at(TokenKind.OpenBracket) && this.atAhead(1, TokenKind.Identifier)) {
443
- const saveIndex = this.index;
444
- const savePos = this.scanner.position;
445
- const savedTokens = this.tokens.slice();
446
- this.nextToken();
447
- const parameters: Parameter[] = [];
448
- let ok = true;
449
- try {
557
+ if (this.at(TokenKind.OpenBracket)) {
558
+ const indexSignature = this.tryParse<TypeElement>(() => {
559
+ const open = this.nextToken();
560
+ const parameters: Parameter[] = [];
450
561
  const paramName = this.parseIdentifier();
451
- let type: TypeNode | undefined;
562
+ let paramType: TypeNode | undefined;
452
563
  if (this.at(TokenKind.Colon)) {
453
564
  this.nextToken();
454
- type = this.parseType();
455
- } else ok = false;
565
+ paramType = this.parseType();
566
+ } else {
567
+ throw new SpeculationError();
568
+ }
456
569
  this.parseExpected(TokenKind.CloseBracket);
457
- parameters.push({ kind: SyntaxKind.Parameter, name: paramName, modifiers: [], dotDotDotToken: false, questionToken: false, type, start: paramName.start, end: type?.end ?? paramName.end });
458
- } catch {
459
- ok = false;
460
- }
461
- if (ok && this.at(TokenKind.Colon)) {
570
+ if (!this.at(TokenKind.Colon)) throw new SpeculationError();
462
571
  this.nextToken();
463
572
  const type = this.parseType();
464
573
  this.parseSemicolonOrComma();
465
- return { kind: SyntaxKind.IndexSignature, parameters, type, start, end: type.end };
466
- }
467
- this.index = saveIndex;
468
- this.tokens = savedTokens;
469
- this.scanner.seek(savePos);
574
+ parameters.push({ kind: SyntaxKind.Parameter, name: paramName, modifiers: [], dotDotDotToken: false, questionToken: false, type: paramType, start: paramName.start, end: paramType?.end ?? paramName.end });
575
+ return { kind: SyntaxKind.IndexSignature, parameters, type, start: open.start, end: type.end };
576
+ });
577
+ if (indexSignature) return indexSignature;
470
578
  }
471
579
  const name = this.parsePropertyName();
472
580
  let questionToken = false;
@@ -180,9 +180,11 @@ export const expressionMethods: ExpressionMethods = {
180
180
  const t = this.lookAhead(i);
181
181
  if (t.kind === TokenKind.EndOfFile) return -1;
182
182
  if (t.kind === TokenKind.OpenParen || t.kind === TokenKind.OpenBracket || t.kind === TokenKind.OpenBrace) depth++;
183
- else if (t.kind === TokenKind.CloseParen || t.kind === TokenKind.CloseBracket || t.kind === TokenKind.CloseBrace) depth--;
184
- else if (t.kind === TokenKind.EqualsGreaterThan && depth === 0) return i;
185
- else if (t.kind === TokenKind.Semicolon && depth === 0) return -1;
183
+ else if (t.kind === TokenKind.CloseParen || t.kind === TokenKind.CloseBracket || t.kind === TokenKind.CloseBrace) {
184
+ if (depth === 0) return -1;
185
+ depth--;
186
+ } else if (t.kind === TokenKind.EqualsGreaterThan && depth === 0) return i;
187
+ else if (depth === 0 && (t.kind === TokenKind.Semicolon || t.kind === TokenKind.Comma)) return -1;
186
188
  }
187
189
  },
188
190
 
@@ -374,7 +376,16 @@ export const expressionMethods: ExpressionMethods = {
374
376
  case TokenKind.NoSubstitutionTemplateLiteral:
375
377
  return this.parseTemplateLiteral();
376
378
  case TokenKind.Slash:
379
+ case TokenKind.RegularExpressionLiteral:
377
380
  return this.parseRegularExpression();
381
+ case TokenKind.ImportKeyword:
382
+ if (this.atAhead(1, TokenKind.Dot)) {
383
+ const importToken = this.nextToken();
384
+ this.nextToken();
385
+ const metaName = this.parseIdentifierName();
386
+ return { kind: SyntaxKind.Identifier, text: "import.meta", start: importToken.start, end: metaName.end };
387
+ }
388
+ break;
378
389
  default:
379
390
  break;
380
391
  }
@@ -422,6 +433,11 @@ export const expressionMethods: ExpressionMethods = {
422
433
  expression = { kind: SyntaxKind.TaggedTemplateExpression, tag: expression, template, start: expression.start, end: template.end };
423
434
  continue;
424
435
  }
436
+ if (token.kind === TokenKind.Exclamation && !token.precededByLineBreak) {
437
+ this.nextToken();
438
+ expression = { kind: SyntaxKind.NonNullExpression, expression, start: expression.start, end: token.end };
439
+ continue;
440
+ }
425
441
  return expression;
426
442
  }
427
443
  },
@@ -13,6 +13,8 @@ export function expressionToPropertyName(expr: Expression): PropertyName | undef
13
13
  return expr;
14
14
  case SyntaxKind.Identifier:
15
15
  return expr;
16
+ case SyntaxKind.PropertyAccessExpression:
17
+ return expr.name;
16
18
  default:
17
19
  return undefined;
18
20
  }
@@ -126,6 +126,11 @@ export const moduleMethods: ModuleMethods = {
126
126
  const assignment: ExportAssignment = { kind: SyntaxKind.ExportAssignment, isExportEquals: false, expression, start, end: expression.end };
127
127
  return assignment;
128
128
  }
129
+ let isTypeOnly = false;
130
+ if (this.at(TokenKind.TypeKeyword) && (this.atAhead(1, TokenKind.OpenBrace) || this.atAhead(1, TokenKind.Asterisk))) {
131
+ isTypeOnly = true;
132
+ this.nextToken();
133
+ }
129
134
  if (this.at(TokenKind.Asterisk)) {
130
135
  this.nextToken();
131
136
  let nsName: Identifier | undefined;
@@ -144,7 +149,7 @@ export const moduleMethods: ModuleMethods = {
144
149
  modifiers: [],
145
150
  exportClause: nsName ? { kind: SyntaxKind.NamespaceImport, name: nsName, start: nsName.start, end: nsName.end } : undefined,
146
151
  moduleSpecifier,
147
- isTypeOnly: false,
152
+ isTypeOnly,
148
153
  start,
149
154
  end: moduleSpecifier?.end ?? nsName?.end ?? start,
150
155
  };
@@ -158,7 +163,7 @@ export const moduleMethods: ModuleMethods = {
158
163
  if (this.at(TokenKind.StringLiteral)) moduleSpecifier = this.stringLiteralFromToken(this.nextToken());
159
164
  }
160
165
  this.parseSemicolon();
161
- const decl: ExportDeclaration = { kind: SyntaxKind.ExportDeclaration, modifiers: [], exportClause: namedExports, moduleSpecifier, isTypeOnly: false, start, end: moduleSpecifier?.end ?? namedExports.end };
166
+ const decl: ExportDeclaration = { kind: SyntaxKind.ExportDeclaration, modifiers: [], exportClause: namedExports, moduleSpecifier, isTypeOnly, start, end: moduleSpecifier?.end ?? namedExports.end };
162
167
  return decl;
163
168
  }
164
169
  const modifiers = this.tryParseModifiers();
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { DiagnosticCode } from "../diagnostics/diagnostic.js";
7
- import { isOfKeyword, TokenKind } from "../lexer/token.js";
7
+ import { isIdentifierNameToken, isKeywordKind, isOfKeyword, TokenKind } from "../lexer/token.js";
8
8
  import {
9
9
  ModifierKind,
10
10
  SyntaxKind,
@@ -58,6 +58,15 @@ export const statementMethods: StatementMethods = {
58
58
  const modifiers: Modifier[] = [];
59
59
  for (;;) {
60
60
  const kind = this.token.kind;
61
+ const nextKind = this.lookAhead(1).kind;
62
+ const nextStartsName =
63
+ isIdentifierNameToken(nextKind) ||
64
+ isKeywordKind(nextKind) ||
65
+ nextKind === TokenKind.OpenBracket ||
66
+ nextKind === TokenKind.StringLiteral ||
67
+ nextKind === TokenKind.NumericLiteral ||
68
+ nextKind === TokenKind.Asterisk ||
69
+ nextKind === TokenKind.OpenBrace;
61
70
  let modifierKind: ModifierKind | undefined;
62
71
  switch (kind) {
63
72
  case TokenKind.ExportKeyword:
@@ -89,6 +98,9 @@ export const statementMethods: StatementMethods = {
89
98
  case TokenKind.ReadonlyKeyword:
90
99
  modifierKind = ModifierKind.Readonly;
91
100
  break;
101
+ case TokenKind.ConstKeyword:
102
+ if (this.atAhead(1, TokenKind.EnumKeyword)) modifierKind = ModifierKind.Const;
103
+ break;
92
104
  case TokenKind.AsyncKeyword:
93
105
  if (!this.lookAhead(1).precededByLineBreak) modifierKind = ModifierKind.Async;
94
106
  break;
@@ -96,6 +108,10 @@ export const statementMethods: StatementMethods = {
96
108
  break;
97
109
  }
98
110
  if (!modifierKind) break;
111
+ // Contextual modifiers (`declare`, `readonly`, `static`, …) are only
112
+ // modifiers when a member/declaration name follows; otherwise they are
113
+ // names themselves (`private declare(...)`, `readonly()`).
114
+ if (modifierKind !== ModifierKind.Export && modifierKind !== ModifierKind.Default && !nextStartsName) break;
99
115
  const token = this.nextToken();
100
116
  modifiers.push({ kind: SyntaxKind.Unknown, modifierKind, start: token.start, end: token.end });
101
117
  }
@@ -120,6 +136,10 @@ export const statementMethods: StatementMethods = {
120
136
  case TokenKind.VarKeyword:
121
137
  case TokenKind.LetKeyword:
122
138
  case TokenKind.ConstKeyword:
139
+ if (this.at(TokenKind.ConstKeyword) && this.atAhead(1, TokenKind.EnumKeyword)) {
140
+ const constToken = this.nextToken();
141
+ return this.parseEnumDeclaration([{ kind: SyntaxKind.Unknown, modifierKind: ModifierKind.Const, start: constToken.start, end: constToken.end }]);
142
+ }
123
143
  return this.parseVariableStatement([]);
124
144
  case TokenKind.FunctionKeyword:
125
145
  return this.parseFunctionDeclaration([]);
@@ -186,6 +206,10 @@ export const statementMethods: StatementMethods = {
186
206
  case TokenKind.VarKeyword:
187
207
  case TokenKind.LetKeyword:
188
208
  case TokenKind.ConstKeyword:
209
+ if (this.at(TokenKind.ConstKeyword) && this.atAhead(1, TokenKind.EnumKeyword)) {
210
+ const constToken = this.nextToken();
211
+ return this.parseEnumDeclaration([...modifiers, { kind: SyntaxKind.Unknown, modifierKind: ModifierKind.Const, start: constToken.start, end: constToken.end }]);
212
+ }
189
213
  return this.parseVariableStatement(modifiers);
190
214
  case TokenKind.FunctionKeyword:
191
215
  return this.parseFunctionDeclaration(modifiers);
@@ -244,9 +268,9 @@ export const statementMethods: StatementMethods = {
244
268
  },
245
269
 
246
270
  parseVariableDeclaration(this: Parser): VariableDeclaration {
247
- const name = this.parseIdentifier();
271
+ const name = this.parseBindingName();
248
272
  let exclamation = false;
249
- if (this.at(TokenKind.Exclamation)) {
273
+ if (name.kind === SyntaxKind.Identifier && this.at(TokenKind.Exclamation)) {
250
274
  this.nextToken();
251
275
  exclamation = true;
252
276
  }
@@ -243,6 +243,11 @@ export const typeMethods: TypeMethods = {
243
243
  const literal: NumericLiteral = { kind: SyntaxKind.NumericLiteral, text: `-${num.text}`, value: -Number(num.value ?? 0), start, end: num.end };
244
244
  return { kind: SyntaxKind.LiteralType, literal, start, end: num.end };
245
245
  }
246
+ case TokenKind.ConstKeyword: {
247
+ // `as const` const assertion: erased like any other type.
248
+ this.nextToken();
249
+ return { kind: SyntaxKind.AnyKeywordType, start, end: token.end };
250
+ }
246
251
  case TokenKind.ImportKeyword: {
247
252
  // import("...").T - parse loosely and degrade to `any`.
248
253
  this.nextToken();