xbintsc 0.2.4 → 0.2.6

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 (124) hide show
  1. package/dist/src/ast/declarations.d.ts +20 -1
  2. package/dist/src/ast/expressions.d.ts +6 -2
  3. package/dist/src/ast/kinds.d.ts +6 -1
  4. package/dist/src/ast/kinds.js +5 -0
  5. package/dist/src/ast/kinds.js.map +1 -1
  6. package/dist/src/ast/statements.d.ts +2 -1
  7. package/dist/src/binder/binder.js +73 -9
  8. package/dist/src/binder/binder.js.map +1 -1
  9. package/dist/src/cli/main.js +2 -4
  10. package/dist/src/cli/main.js.map +1 -1
  11. package/dist/src/codegen/generator/calls.d.ts +1 -1
  12. package/dist/src/codegen/generator/calls.js +57 -14
  13. package/dist/src/codegen/generator/calls.js.map +1 -1
  14. package/dist/src/codegen/generator/context.d.ts +1 -1
  15. package/dist/src/codegen/generator/expressions.js +39 -3
  16. package/dist/src/codegen/generator/expressions.js.map +1 -1
  17. package/dist/src/codegen/generator/module.js +73 -7
  18. package/dist/src/codegen/generator/module.js.map +1 -1
  19. package/dist/src/codegen/generator/state.js +16 -0
  20. package/dist/src/codegen/generator/state.js.map +1 -1
  21. package/dist/src/codegen/generator/statements.d.ts +4 -1
  22. package/dist/src/codegen/generator/statements.js +141 -10
  23. package/dist/src/codegen/generator/statements.js.map +1 -1
  24. package/dist/src/codegen/generator/tables.d.ts +3 -0
  25. package/dist/src/codegen/generator/tables.js +13 -0
  26. package/dist/src/codegen/generator/tables.js.map +1 -1
  27. package/dist/src/codegen/values.d.ts +18 -11
  28. package/dist/src/codegen/values.js +92 -15
  29. package/dist/src/codegen/values.js.map +1 -1
  30. package/dist/src/diagnostics/source.d.ts +1 -1
  31. package/dist/src/diagnostics/source.js +12 -7
  32. package/dist/src/diagnostics/source.js.map +1 -1
  33. package/dist/src/driver/compiler.d.ts +5 -0
  34. package/dist/src/driver/compiler.js +42 -2
  35. package/dist/src/driver/compiler.js.map +1 -1
  36. package/dist/src/driver/modules.js +29 -4
  37. package/dist/src/driver/modules.js.map +1 -1
  38. package/dist/src/extensions/node/child_process/index.d.ts +7 -0
  39. package/dist/src/extensions/node/child_process/index.js +19 -0
  40. package/dist/src/extensions/node/child_process/index.js.map +1 -0
  41. package/dist/src/extensions/node/crypto/index.d.ts +6 -0
  42. package/dist/src/extensions/node/crypto/index.js +18 -0
  43. package/dist/src/extensions/node/crypto/index.js.map +1 -0
  44. package/dist/src/extensions/node/index.js +6 -0
  45. package/dist/src/extensions/node/index.js.map +1 -1
  46. package/dist/src/extensions/node/module.d.ts +9 -1
  47. package/dist/src/extensions/node/module.js +14 -1
  48. package/dist/src/extensions/node/module.js.map +1 -1
  49. package/dist/src/extensions/node/url/index.d.ts +7 -0
  50. package/dist/src/extensions/node/url/index.js +21 -0
  51. package/dist/src/extensions/node/url/index.js.map +1 -0
  52. package/dist/src/lexer/scanner.js +4 -5
  53. package/dist/src/lexer/scanner.js.map +1 -1
  54. package/dist/src/lexer/token.d.ts +10 -0
  55. package/dist/src/lexer/token.js +69 -0
  56. package/dist/src/lexer/token.js.map +1 -1
  57. package/dist/src/parser/context.js +4 -16
  58. package/dist/src/parser/context.js.map +1 -1
  59. package/dist/src/parser/declarations.d.ts +2 -0
  60. package/dist/src/parser/declarations.js +146 -33
  61. package/dist/src/parser/declarations.js.map +1 -1
  62. package/dist/src/parser/expressions.js +20 -3
  63. package/dist/src/parser/expressions.js.map +1 -1
  64. package/dist/src/parser/helpers.js +2 -0
  65. package/dist/src/parser/helpers.js.map +1 -1
  66. package/dist/src/parser/modules.js +7 -2
  67. package/dist/src/parser/modules.js.map +1 -1
  68. package/dist/src/parser/statements.js +28 -3
  69. package/dist/src/parser/statements.js.map +1 -1
  70. package/dist/src/parser/types.js +5 -0
  71. package/dist/src/parser/types.js.map +1 -1
  72. package/dist/tests/codegen/llvm.test.js +16 -0
  73. package/dist/tests/codegen/llvm.test.js.map +1 -1
  74. package/dist/tests/driver/compiler.test.js +13 -1
  75. package/dist/tests/driver/compiler.test.js.map +1 -1
  76. package/dist/tests/e2e/compile.test.js +57 -0
  77. package/dist/tests/e2e/compile.test.js.map +1 -1
  78. package/dist/tests/parser/parser.test.js +53 -0
  79. package/dist/tests/parser/parser.test.js.map +1 -1
  80. package/package.json +1 -1
  81. package/runtime/ext_node/child_process/child_process.c +213 -0
  82. package/runtime/ext_node/crypto/crypto.c +177 -0
  83. package/runtime/ext_node/process/process.c +37 -3
  84. package/runtime/ext_node/url/url.c +85 -0
  85. package/runtime/rt.h +20 -0
  86. package/runtime/rt_internal.h +11 -0
  87. package/runtime/xt_builtins.c +23 -0
  88. package/runtime/xt_containers.c +136 -10
  89. package/runtime/xt_io.c +53 -0
  90. package/runtime/xt_regex.h +16 -6
  91. package/runtime/xt_stdlib.c +11 -2
  92. package/runtime/xt_stdlib2.c +250 -13
  93. package/runtime/xt_values.c +2 -1
  94. package/src/ast/declarations.ts +26 -1
  95. package/src/ast/expressions.ts +7 -2
  96. package/src/ast/kinds.ts +5 -0
  97. package/src/ast/statements.ts +2 -1
  98. package/src/binder/binder.ts +75 -9
  99. package/src/cli/main.ts +2 -3
  100. package/src/codegen/generator/calls.ts +59 -16
  101. package/src/codegen/generator/context.ts +1 -1
  102. package/src/codegen/generator/expressions.ts +39 -3
  103. package/src/codegen/generator/module.ts +79 -7
  104. package/src/codegen/generator/state.ts +16 -0
  105. package/src/codegen/generator/statements.ts +139 -11
  106. package/src/codegen/generator/tables.ts +14 -0
  107. package/src/codegen/values.ts +85 -16
  108. package/src/diagnostics/source.ts +12 -7
  109. package/src/driver/compiler.ts +44 -2
  110. package/src/driver/modules.ts +28 -3
  111. package/src/extensions/node/child_process/index.ts +24 -0
  112. package/src/extensions/node/crypto/index.ts +23 -0
  113. package/src/extensions/node/index.ts +6 -0
  114. package/src/extensions/node/module.ts +13 -1
  115. package/src/extensions/node/url/index.ts +26 -0
  116. package/src/lexer/scanner.ts +4 -4
  117. package/src/lexer/token.ts +73 -0
  118. package/src/parser/context.ts +4 -15
  119. package/src/parser/declarations.ts +140 -29
  120. package/src/parser/expressions.ts +20 -4
  121. package/src/parser/helpers.ts +2 -0
  122. package/src/parser/modules.ts +7 -2
  123. package/src/parser/statements.ts +27 -3
  124. package/src/parser/types.ts +5 -0
@@ -240,12 +240,12 @@ export class Scanner {
240
240
  const cleaned = raw.replace(/_/g, "");
241
241
  if (isBigInt) {
242
242
  raw = raw.slice(0, -1).replace(/_/g, "");
243
- try {
244
- return this.makeToken(TokenKind.BigIntLiteral, start, this.pos, raw, precededByLineBreak, BigInt(raw));
245
- } catch {
243
+ const parsed = Number(raw);
244
+ if (parsed !== parsed) {
246
245
  this.error(DiagnosticCode.InvalidNumber, `Invalid BigInt literal '${raw}'`, start, this.pos);
247
- return this.makeToken(TokenKind.BigIntLiteral, start, this.pos, raw, precededByLineBreak, 0n);
246
+ return this.makeToken(TokenKind.BigIntLiteral, start, this.pos, raw, precededByLineBreak, 0);
248
247
  }
248
+ return this.makeToken(TokenKind.BigIntLiteral, start, this.pos, raw, precededByLineBreak, parsed);
249
249
  }
250
250
  let value: number;
251
251
  if (/^0[xX]/.test(cleaned)) value = parseInt(cleaned.slice(2), 16);
@@ -264,3 +264,76 @@ export const CONTEXTUAL_KEYWORDS: ReadonlySet<TokenKind> = new Set([
264
264
  export function isOfKeyword(token: Token): boolean {
265
265
  return token.kind === TokenKind.Identifier && token.text === "of";
266
266
  }
267
+
268
+ /** Every keyword token kind, derived from the scanner's keyword table. */
269
+ export const KEYWORD_KINDS: ReadonlySet<TokenKind> = new Set(KEYWORDS.values());
270
+
271
+ /**
272
+ * True for the hard/contextual keywords the scanner emits. Property names and
273
+ * member access allow any of these (`obj.default`, `{ type: 1 }`), so the
274
+ * parser consults this instead of maintaining a hand-written allow list.
275
+ */
276
+ export function isKeywordKind(kind: TokenKind): boolean {
277
+ return KEYWORD_KINDS.has(kind);
278
+ }
279
+
280
+ /**
281
+ * Keywords that can never be used as a plain binding/identifier. Everything
282
+ * else the scanner recognizes is a TypeScript contextual keyword (`type`,
283
+ * `async`, `module`, `namespace`, `readonly`, …) and may legally name a
284
+ * variable, parameter or property.
285
+ */
286
+ const RESERVED_KEYWORDS: ReadonlySet<TokenKind> = new Set([
287
+ TokenKind.BreakKeyword,
288
+ TokenKind.CaseKeyword,
289
+ TokenKind.CatchKeyword,
290
+ TokenKind.ClassKeyword,
291
+ TokenKind.ConstKeyword,
292
+ TokenKind.ContinueKeyword,
293
+ TokenKind.DebuggerKeyword,
294
+ TokenKind.DefaultKeyword,
295
+ TokenKind.DeleteKeyword,
296
+ TokenKind.DoKeyword,
297
+ TokenKind.ElseKeyword,
298
+ TokenKind.EnumKeyword,
299
+ TokenKind.ExportKeyword,
300
+ TokenKind.ExtendsKeyword,
301
+ TokenKind.FalseKeyword,
302
+ TokenKind.FinallyKeyword,
303
+ TokenKind.ForKeyword,
304
+ TokenKind.FunctionKeyword,
305
+ TokenKind.IfKeyword,
306
+ TokenKind.ImportKeyword,
307
+ TokenKind.InKeyword,
308
+ TokenKind.InstanceOfKeyword,
309
+ TokenKind.NewKeyword,
310
+ TokenKind.NullKeyword,
311
+ TokenKind.ReturnKeyword,
312
+ TokenKind.SuperKeyword,
313
+ TokenKind.SwitchKeyword,
314
+ TokenKind.ThisKeyword,
315
+ TokenKind.ThrowKeyword,
316
+ TokenKind.TrueKeyword,
317
+ TokenKind.TryKeyword,
318
+ TokenKind.TypeOfKeyword,
319
+ TokenKind.VarKeyword,
320
+ TokenKind.VoidKeyword,
321
+ TokenKind.WhileKeyword,
322
+ TokenKind.WithKeyword,
323
+ // Strict-mode / module reserved words.
324
+ TokenKind.LetKeyword,
325
+ TokenKind.StaticKeyword,
326
+ TokenKind.YieldKeyword,
327
+ TokenKind.AwaitKeyword,
328
+ TokenKind.ImplementsKeyword,
329
+ TokenKind.InterfaceKeyword,
330
+ TokenKind.PackageKeyword,
331
+ TokenKind.PrivateKeyword,
332
+ TokenKind.ProtectedKeyword,
333
+ TokenKind.PublicKeyword,
334
+ ]);
335
+
336
+ /** True when the scanner token may be used as an identifier/binding name. */
337
+ export function isIdentifierNameToken(kind: TokenKind): boolean {
338
+ return kind === TokenKind.Identifier || (isKeywordKind(kind) && !RESERVED_KEYWORDS.has(kind));
339
+ }
@@ -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,15 @@ 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
+ let isThisParameter = false;
161
+ if (this.at(TokenKind.ThisKeyword)) {
162
+ const thisToken = this.nextToken();
163
+ name = { kind: SyntaxKind.Identifier, text: "this", start: thisToken.start, end: thisToken.end };
164
+ isThisParameter = true;
165
+ } else {
166
+ name = this.parseBindingName();
167
+ }
157
168
  let questionToken = false;
158
169
  if (this.at(TokenKind.Question)) {
159
170
  this.nextToken();
@@ -169,7 +180,11 @@ export const declarationMethods: DeclarationMethods = {
169
180
  this.nextToken();
170
181
  initializer = this.parseAssignmentExpression();
171
182
  }
172
- params.push({ kind: SyntaxKind.Parameter, name, modifiers, dotDotDotToken, questionToken, type, initializer, start, end: initializer?.end ?? type?.end ?? name.end });
183
+ // A `this` parameter is a type-level annotation only and is never a
184
+ // runtime argument, so it must not occupy an `argv` slot.
185
+ if (!isThisParameter) {
186
+ params.push({ kind: SyntaxKind.Parameter, name, modifiers, dotDotDotToken, questionToken, type, initializer, start, end: initializer?.end ?? type?.end ?? name.end });
187
+ }
173
188
  if (this.at(TokenKind.Comma)) this.nextToken();
174
189
  else break;
175
190
  }
@@ -177,6 +192,76 @@ export const declarationMethods: DeclarationMethods = {
177
192
  return params;
178
193
  },
179
194
 
195
+ parseBindingName(this: Parser): import("../ast/declarations.js").BindingName {
196
+ if (this.at(TokenKind.OpenBracket)) {
197
+ const open = this.nextToken();
198
+ const elements: (import("../ast/declarations.js").BindingElement | undefined)[] = [];
199
+ while (!this.at(TokenKind.CloseBracket) && !this.at(TokenKind.EndOfFile)) {
200
+ if (this.at(TokenKind.Comma)) {
201
+ elements.push(undefined);
202
+ this.nextToken();
203
+ continue;
204
+ }
205
+ elements.push(this.parseBindingElement(false));
206
+ if (this.at(TokenKind.Comma)) this.nextToken();
207
+ else break;
208
+ }
209
+ const close = this.parseExpected(TokenKind.CloseBracket);
210
+ return { kind: SyntaxKind.ArrayBindingPattern, elements, start: open.start, end: close.end };
211
+ }
212
+ if (this.at(TokenKind.OpenBrace)) {
213
+ const open = this.nextToken();
214
+ const elements: import("../ast/declarations.js").BindingElement[] = [];
215
+ while (!this.at(TokenKind.CloseBrace) && !this.at(TokenKind.EndOfFile)) {
216
+ elements.push(this.parseBindingElement(true));
217
+ if (this.at(TokenKind.Comma)) this.nextToken();
218
+ else break;
219
+ }
220
+ const close = this.parseExpected(TokenKind.CloseBrace);
221
+ return { kind: SyntaxKind.ObjectBindingPattern, elements, start: open.start, end: close.end };
222
+ }
223
+ return this.parseIdentifier();
224
+ },
225
+
226
+ parseBindingElement(this: Parser, allowPropertyName: boolean): import("../ast/declarations.js").BindingElement {
227
+ const start = this.token.start;
228
+ let dotDotDotToken = false;
229
+ if (this.at(TokenKind.DotDotDot)) {
230
+ this.nextToken();
231
+ dotDotDotToken = true;
232
+ }
233
+ let propertyName: PropertyName | undefined;
234
+ let name: import("../ast/declarations.js").BindingName;
235
+ if (allowPropertyName && this.at(TokenKind.OpenBracket)) {
236
+ // Computed property key: `{ [expr]: target }`.
237
+ this.nextToken();
238
+ const expr = this.parseAssignmentExpression();
239
+ this.parseExpected(TokenKind.CloseBracket);
240
+ propertyName = expressionToPropertyName(expr) ?? { kind: SyntaxKind.Identifier, text: "<computed>", start: expr.start, end: expr.end };
241
+ this.parseExpected(TokenKind.Colon);
242
+ name = this.parseBindingName();
243
+ } else if (allowPropertyName && !this.at(TokenKind.OpenBrace) && !this.at(TokenKind.OpenBracket)) {
244
+ const first = this.parseIdentifierName();
245
+ if (this.at(TokenKind.Colon)) {
246
+ this.nextToken();
247
+ propertyName = first;
248
+ name = this.parseBindingName();
249
+ } else {
250
+ name = first;
251
+ }
252
+ } else {
253
+ // Array element, or a nested pattern used as an object value.
254
+ name = this.parseBindingName();
255
+ }
256
+ let initializer: Expression | undefined;
257
+ if (this.at(TokenKind.Equals)) {
258
+ this.nextToken();
259
+ initializer = this.parseAssignmentExpression();
260
+ }
261
+ const end = initializer?.end ?? (name.kind === SyntaxKind.Identifier ? name.end : name.end);
262
+ return { kind: SyntaxKind.BindingElement, name, propertyName, dotDotDotToken, initializer, start, end };
263
+ },
264
+
180
265
  parseReturnType(this: Parser): TypeNode {
181
266
  if (this.at(TokenKind.AssertsKeyword)) {
182
267
  const start = this.nextToken().start;
@@ -249,6 +334,42 @@ export const declarationMethods: DeclarationMethods = {
249
334
  this.nextToken();
250
335
  return undefined;
251
336
  }
337
+ if (
338
+ (this.at(TokenKind.GetKeyword) || this.at(TokenKind.SetKeyword)) &&
339
+ !this.atAhead(1, TokenKind.OpenParen) &&
340
+ !this.atAhead(1, TokenKind.Colon) &&
341
+ !this.atAhead(1, TokenKind.Equals) &&
342
+ !this.atAhead(1, TokenKind.Question) &&
343
+ !this.atAhead(1, TokenKind.LessThan) &&
344
+ !this.atAhead(1, TokenKind.Semicolon)
345
+ ) {
346
+ const accessorToken = this.nextToken();
347
+ const name = this.parsePropertyName();
348
+ const parameters = this.parseParameters();
349
+ let returnType: TypeNode | undefined;
350
+ if (this.at(TokenKind.Colon)) {
351
+ this.nextToken();
352
+ returnType = this.parseReturnType();
353
+ }
354
+ let body: Block | undefined;
355
+ if (this.at(TokenKind.OpenBrace)) body = this.parseBlock();
356
+ else this.parseSemicolon();
357
+ const accessor: MethodDeclaration = {
358
+ kind: SyntaxKind.MethodDeclaration,
359
+ name,
360
+ modifiers,
361
+ typeParameters: [],
362
+ parameters,
363
+ returnType,
364
+ body,
365
+ flags: NodeFlags.None,
366
+ optional: false,
367
+ accessor: accessorToken.kind === TokenKind.GetKeyword ? "get" : "set",
368
+ start,
369
+ end: body?.end ?? returnType?.end ?? name.end,
370
+ };
371
+ return accessor;
372
+ }
252
373
  if (this.at(TokenKind.ConstructorKeyword)) {
253
374
  const name = this.nextToken();
254
375
  const parameters = this.parseParameters();
@@ -337,13 +458,10 @@ export const declarationMethods: DeclarationMethods = {
337
458
  return { kind: SyntaxKind.NumericLiteral, text: token.text, value: Number(token.value ?? 0), start: token.start, end: token.end };
338
459
  }
339
460
  if (token.kind === TokenKind.OpenBracket) {
340
- this.nextToken();
461
+ const start = this.nextToken().start;
341
462
  const expr = this.parseAssignmentExpression();
342
- this.parseExpected(TokenKind.CloseBracket);
343
- const name = expressionToPropertyName(expr);
344
- if (name) return name;
345
- this.error(DiagnosticCode.InvalidTypeSyntax, "Computed property names are not supported by xbintsc");
346
- return { kind: SyntaxKind.Identifier, text: "<computed>", start: expr.start, end: expr.end };
463
+ const close = this.parseExpected(TokenKind.CloseBracket);
464
+ return { kind: SyntaxKind.ComputedPropertyName, expression: expr, start, end: close.end };
347
465
  }
348
466
  return this.parseIdentifierName();
349
467
  },
@@ -439,34 +557,27 @@ export const declarationMethods: DeclarationMethods = {
439
557
  const name: Identifier = { kind: SyntaxKind.Identifier, text: "__call", start, end: start };
440
558
  return { kind: SyntaxKind.MethodSignature, name, questionToken: false, typeParameters, parameters, returnType, start, end: returnType?.end ?? start };
441
559
  }
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 {
560
+ if (this.at(TokenKind.OpenBracket)) {
561
+ const indexSignature = this.tryParse<TypeElement>(() => {
562
+ const open = this.nextToken();
563
+ const parameters: Parameter[] = [];
450
564
  const paramName = this.parseIdentifier();
451
- let type: TypeNode | undefined;
565
+ let paramType: TypeNode | undefined;
452
566
  if (this.at(TokenKind.Colon)) {
453
567
  this.nextToken();
454
- type = this.parseType();
455
- } else ok = false;
568
+ paramType = this.parseType();
569
+ } else {
570
+ throw new SpeculationError();
571
+ }
456
572
  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)) {
573
+ if (!this.at(TokenKind.Colon)) throw new SpeculationError();
462
574
  this.nextToken();
463
575
  const type = this.parseType();
464
576
  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);
577
+ parameters.push({ kind: SyntaxKind.Parameter, name: paramName, modifiers: [], dotDotDotToken: false, questionToken: false, type: paramType, start: paramName.start, end: paramType?.end ?? paramName.end });
578
+ return { kind: SyntaxKind.IndexSignature, parameters, type, start: open.start, end: type.end };
579
+ });
580
+ if (indexSignature) return indexSignature;
470
581
  }
471
582
  const name = this.parsePropertyName();
472
583
  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
 
@@ -329,7 +331,7 @@ export const expressionMethods: ExpressionMethods = {
329
331
  }
330
332
  case TokenKind.BigIntLiteral:
331
333
  this.nextToken();
332
- return { kind: SyntaxKind.BigIntLiteral, text: token.text, value: BigInt(token.value ?? 0n), start: token.start, end: token.end };
334
+ return { kind: SyntaxKind.BigIntLiteral, text: token.text, value: Number(token.value ?? 0), start: token.start, end: token.end };
333
335
  case TokenKind.StringLiteral:
334
336
  this.nextToken();
335
337
  return this.stringLiteralFromToken(token);
@@ -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();