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
@@ -14,7 +14,7 @@ import type { TypeElement } from "./types.js";
14
14
 
15
15
  export interface Parameter extends Node {
16
16
  readonly kind: SyntaxKind.Parameter;
17
- readonly name: Identifier;
17
+ readonly name: BindingName;
18
18
  readonly modifiers: Modifier[];
19
19
  readonly dotDotDotToken: boolean;
20
20
  readonly questionToken: boolean;
@@ -22,6 +22,29 @@ export interface Parameter extends Node {
22
22
  readonly initializer?: Expression;
23
23
  }
24
24
 
25
+ // -- binding patterns (destructuring) --------------------------------------
26
+
27
+ export type BindingName = Identifier | ArrayBindingPattern | ObjectBindingPattern;
28
+
29
+ export interface BindingElement extends Node {
30
+ readonly kind: SyntaxKind.BindingElement;
31
+ readonly name: BindingName;
32
+ /** For `{ a: b }` the `a`; absent for shorthand and array patterns. */
33
+ readonly propertyName?: PropertyName;
34
+ readonly dotDotDotToken: boolean;
35
+ readonly initializer?: Expression;
36
+ }
37
+
38
+ export interface ArrayBindingPattern extends Node {
39
+ readonly kind: SyntaxKind.ArrayBindingPattern;
40
+ readonly elements: (BindingElement | undefined)[];
41
+ }
42
+
43
+ export interface ObjectBindingPattern extends Node {
44
+ readonly kind: SyntaxKind.ObjectBindingPattern;
45
+ readonly elements: BindingElement[];
46
+ }
47
+
25
48
  export interface TypeParameterDeclaration extends Node {
26
49
  readonly kind: SyntaxKind.TypeParameterDeclaration;
27
50
  readonly name: Identifier;
@@ -74,6 +97,8 @@ export interface MethodDeclaration extends Node {
74
97
  readonly body?: Block;
75
98
  readonly flags: NodeFlags;
76
99
  readonly optional: boolean;
100
+ /** Present for `get x()` / `set x(v)` accessors. */
101
+ readonly accessor?: "get" | "set";
77
102
  }
78
103
 
79
104
  export interface ConstructorDeclaration extends Node {
@@ -40,7 +40,7 @@ export interface NumericLiteral extends Node {
40
40
  export interface BigIntLiteral extends Node {
41
41
  readonly kind: SyntaxKind.BigIntLiteral;
42
42
  readonly text: string;
43
- readonly value: bigint;
43
+ readonly value: number;
44
44
  }
45
45
 
46
46
  export interface StringLiteral extends Node {
@@ -127,7 +127,12 @@ export interface ObjectLiteralExpression extends Node {
127
127
  readonly properties: ObjectLiteralElementLike[];
128
128
  }
129
129
 
130
- export type PropertyName = Identifier | StringLiteral | NumericLiteral | PrivateIdentifier;
130
+ export type PropertyName = Identifier | StringLiteral | NumericLiteral | PrivateIdentifier | ComputedPropertyName;
131
+
132
+ export interface ComputedPropertyName extends Node {
133
+ readonly kind: SyntaxKind.ComputedPropertyName;
134
+ readonly expression: Expression;
135
+ }
131
136
 
132
137
  export interface FunctionExpression extends Node {
133
138
  readonly kind: SyntaxKind.FunctionExpression;
package/src/ast/kinds.ts CHANGED
@@ -59,6 +59,10 @@ export const enum SyntaxKind {
59
59
  AwaitExpression = 43,
60
60
  YieldExpression = 44,
61
61
  TaggedTemplateExpression = 45,
62
+ ArrayBindingPattern = 46,
63
+ ObjectBindingPattern = 47,
64
+ BindingElement = 48,
65
+ ComputedPropertyName = 49,
62
66
 
63
67
  // Statements
64
68
  ExpressionStatement = 60,
@@ -173,4 +177,5 @@ export const enum ModifierKind {
173
177
  Static = "static",
174
178
  Readonly = "readonly",
175
179
  Async = "async",
180
+ Const = "const",
176
181
  }
@@ -5,6 +5,7 @@
5
5
  import { SyntaxKind } from "./kinds.js";
6
6
  import type { Expression, Modifier, Node, Statement, TypeNode } from "./common.js";
7
7
  import type { Identifier } from "./expressions.js";
8
+ import type { BindingName } from "./declarations.js";
8
9
 
9
10
  // ---------------------------------------------------------------------------
10
11
  // Statements
@@ -30,7 +31,7 @@ export interface DebuggerStatement extends Node {
30
31
 
31
32
  export interface VariableDeclaration extends Node {
32
33
  readonly kind: SyntaxKind.VariableDeclaration;
33
- readonly name: Identifier;
34
+ readonly name: BindingName;
34
35
  readonly exclamation: boolean;
35
36
  readonly type?: TypeNode;
36
37
  readonly initializer?: Expression;
@@ -13,6 +13,7 @@ import { NodeFlags, ModifierKind } from "../ast/kinds.js";
13
13
  import {
14
14
  SyntaxKind,
15
15
  type ArrowFunction,
16
+ type BindingName,
16
17
  type Block,
17
18
  type ClassDeclaration,
18
19
  type ClassExpression,
@@ -244,6 +245,27 @@ class Binder {
244
245
  return { kind, node, parent, fn: this.current, symbols: new Map() };
245
246
  }
246
247
 
248
+ /** Declare every identifier introduced by a (possibly destructured) binding. */
249
+ private declareBinding(
250
+ name: BindingName,
251
+ kind: SymbolKind,
252
+ declaration: Node,
253
+ scope: Scope,
254
+ mutable: boolean,
255
+ ): SymbolInfo | undefined {
256
+ if (name.kind === SyntaxKind.Identifier) {
257
+ return this.declare(name.text, kind, declaration, scope, mutable);
258
+ }
259
+ let first: SymbolInfo | undefined;
260
+ const pattern = name as import("../ast/declarations.js").ArrayBindingPattern | import("../ast/declarations.js").ObjectBindingPattern;
261
+ for (const element of pattern.elements) {
262
+ if (!element) continue;
263
+ const symbol = this.declareBinding(element.name, kind, element.name, scope, mutable);
264
+ if (!first) first = symbol;
265
+ }
266
+ return first;
267
+ }
268
+
247
269
  private declare(
248
270
  name: string,
249
271
  kind: SymbolKind,
@@ -334,7 +356,7 @@ class Binder {
334
356
  ? SymbolKind.Var
335
357
  : SymbolKind.Let;
336
358
  for (const declaration of list.declarations) {
337
- this.declare(declaration.name.text, kind, declaration, scope, kind !== SymbolKind.Const);
359
+ this.declareBinding(declaration.name, kind, declaration, scope, kind !== SymbolKind.Const);
338
360
  }
339
361
  }
340
362
 
@@ -369,9 +391,9 @@ class Binder {
369
391
  const decl = node as VariableStatement;
370
392
  if (decl.declarationList.declarationKind === "var") {
371
393
  for (const d of decl.declarationList.declarations) {
372
- const existing = fn.scope.symbols.get(d.name.text);
373
- if (!existing) this.declare(d.name.text, SymbolKind.Var, d, fn.scope, true);
374
- else this.symbolOfDeclaration.set(d, existing);
394
+ const existing = d.name.kind === SyntaxKind.Identifier ? fn.scope.symbols.get(d.name.text) : undefined;
395
+ if (!existing) this.declareBinding(d.name, SymbolKind.Var, d, fn.scope, true);
396
+ else if (d.name.kind === SyntaxKind.Identifier) this.symbolOfDeclaration.set(d, existing);
375
397
  }
376
398
  }
377
399
  return;
@@ -432,6 +454,35 @@ class Binder {
432
454
  case SyntaxKind.Identifier:
433
455
  this.reference(node as Identifier, scope);
434
456
  return;
457
+ case SyntaxKind.PropertyAccessExpression:
458
+ // `obj.name` reads a property, not a variable named `name`.
459
+ this.bindNode((node as unknown as { expression: Expression }).expression, scope);
460
+ return;
461
+ case SyntaxKind.ElementAccessExpression: {
462
+ const access = node as unknown as { expression: Expression; argumentExpression: Expression };
463
+ this.bindNode(access.expression, scope);
464
+ this.bindNode(access.argumentExpression, scope);
465
+ return;
466
+ }
467
+ case SyntaxKind.PropertyAssignment: {
468
+ // `{ key: value }` / `{ key() {} }`: only the value is an expression,
469
+ // except a computed key (`{ [expr]: value }`) whose expression is real.
470
+ const property = node as unknown as { name: PropertyName; initializer: Expression };
471
+ if (property.name.kind === SyntaxKind.ComputedPropertyName) {
472
+ this.bindNode((property.name as unknown as { expression: Expression }).expression, scope);
473
+ }
474
+ this.bindNode(property.initializer, scope);
475
+ return;
476
+ }
477
+ case SyntaxKind.ShorthandPropertyAssignment:
478
+ this.reference((node as unknown as { name: Identifier }).name, scope);
479
+ return;
480
+ case SyntaxKind.LabeledStatement:
481
+ this.bindNode((node as unknown as { statement: Node }).statement, scope);
482
+ return;
483
+ case SyntaxKind.BreakStatement:
484
+ case SyntaxKind.ContinueStatement:
485
+ return;
435
486
  case SyntaxKind.ThisKeyword:
436
487
  this.current.usesThis = true;
437
488
  return;
@@ -506,11 +557,26 @@ class Binder {
506
557
  private bindVariableList(list: VariableDeclarationList, scope: Scope): void {
507
558
  for (const declaration of list.declarations) {
508
559
  const symbol = this.symbolOfDeclaration.get(declaration);
509
- if (declaration.name) this.symbolOfDeclaration.set(declaration.name, symbol ?? this.symbolOfDeclaration.get(declaration) as SymbolInfo);
560
+ if (declaration.name.kind === SyntaxKind.Identifier) {
561
+ this.symbolOfDeclaration.set(declaration.name, symbol ?? this.symbolOfDeclaration.get(declaration) as SymbolInfo);
562
+ } else {
563
+ this.bindBindingPattern(declaration.name, scope);
564
+ }
510
565
  this.bindNode(declaration.initializer, scope);
511
566
  }
512
567
  }
513
568
 
569
+ /** Bind default-value expressions inside a binding pattern. */
570
+ private bindBindingPattern(name: BindingName, scope: Scope): void {
571
+ if (name.kind === SyntaxKind.Identifier) return;
572
+ const pattern = name as import("../ast/declarations.js").ArrayBindingPattern | import("../ast/declarations.js").ObjectBindingPattern;
573
+ for (const element of pattern.elements) {
574
+ if (!element) continue;
575
+ if (element.initializer) this.bindNode(element.initializer, scope);
576
+ this.bindBindingPattern(element.name, scope);
577
+ }
578
+ }
579
+
514
580
  private bindFunction(
515
581
  node: FunctionDeclaration | FunctionExpression | ArrowFunction,
516
582
  parentScope: Scope,
@@ -533,8 +599,8 @@ class Binder {
533
599
  // Parameters live in the function scope.
534
600
  const params: SymbolInfo[] = [];
535
601
  for (const parameter of node.parameters) {
536
- const paramSymbol = this.declare(parameter.name.text, SymbolKind.Parameter, parameter, scope, true);
537
- params.push(paramSymbol);
602
+ const paramSymbol = this.declareBinding(parameter.name, SymbolKind.Parameter, parameter, scope, true);
603
+ if (paramSymbol) params.push(paramSymbol);
538
604
  if (parameter.initializer) this.bindNode(parameter.initializer, scope);
539
605
  }
540
606
  (fn as { params: SymbolInfo[] }).params = params;
@@ -651,8 +717,8 @@ class Binder {
651
717
 
652
718
  const params: SymbolInfo[] = [];
653
719
  for (const parameter of parameters) {
654
- const paramSymbol = this.declare(parameter.name.text, SymbolKind.Parameter, parameter, scope, true);
655
- params.push(paramSymbol);
720
+ const paramSymbol = this.declareBinding(parameter.name, SymbolKind.Parameter, parameter, scope, true);
721
+ if (paramSymbol) params.push(paramSymbol);
656
722
  if (parameter.initializer) this.bindNode(parameter.initializer, scope);
657
723
  }
658
724
  (fn as { params: SymbolInfo[] }).params = params;
package/src/cli/main.ts CHANGED
@@ -16,7 +16,7 @@ import { resolve } from "node:path";
16
16
  import { pathToFileURL } from "node:url";
17
17
  import { DiagnosticBag, formatDiagnostic, type Diagnostic } from "../diagnostics/diagnostic.js";
18
18
  import { SourceFile } from "../diagnostics/source.js";
19
- import { build, compileString, COMPILER_VERSION, type EmitKind } from "../driver/compiler.js";
19
+ import { build, compileEntry, COMPILER_VERSION, type EmitKind } from "../driver/compiler.js";
20
20
  import { createDefaultRegistry, type ExtensionRegistry } from "../extensions/registry.js";
21
21
  import { nodeExtension } from "../extensions/node/index.js";
22
22
 
@@ -152,9 +152,8 @@ export function run(argv: readonly string[], io: CliIo = defaultIo): number {
152
152
  io.stderr("xbintsc: emit requires a source file\n");
153
153
  return 1;
154
154
  }
155
- const source = readFileSync(resolve(entry), "utf8");
156
155
  const registry = buildRegistry(args.flags);
157
- const { ir, diagnostics } = compileString(source, entry, registry);
156
+ const { ir, diagnostics } = compileEntry(entry, registry);
158
157
  if (diagnostics.some((d) => d.category === "error")) {
159
158
  printDiagnostics(diagnostics, entry, io);
160
159
  return 1;
@@ -14,6 +14,7 @@ import {
14
14
  type Identifier,
15
15
  type ObjectLiteralExpression,
16
16
  type PropertyAccessExpression,
17
+ type SpreadElement,
17
18
  } from "../../ast/nodes.js";
18
19
  import { SymbolKind, type FunctionInfo } from "../../binder/binder.js";
19
20
  import type { ModuleExport } from "../../extensions/registry.js";
@@ -35,7 +36,7 @@ export interface CallMethods {
35
36
  emitCall(this: Generator, node: CallExpression): string;
36
37
  tryEmitBuiltinCall(this: Generator, node: CallExpression, callee: PropertyAccessExpression): string | undefined;
37
38
  runtimeCall(this: Generator, name: string, args: readonly string[]): string;
38
- emitArguments(this: Generator, args: readonly Expression[]): { argc: number; ptr: string };
39
+ emitArguments(this: Generator, args: readonly Expression[]): { argc: string; ptr: string };
39
40
  emitDelete(this: Generator, node: DeleteExpression): string;
40
41
  emitPropertyAccess(this: Generator, node: PropertyAccessExpression): string;
41
42
  emitElementAccess(this: Generator, node: ElementAccessExpression): string;
@@ -262,17 +263,38 @@ export const callMethods: CallMethods = {
262
263
  return result;
263
264
  },
264
265
 
265
- emitArguments(args: readonly Expression[]): { argc: number; ptr: string } {
266
- if (args.length === 0) return { argc: 0, ptr: "null" };
267
- const ptr = `%args${this.current.allocas.length}`;
268
- this.current.allocas.push(`${ptr} = alloca i64, i32 ${args.length}`);
269
- for (let index = 0; index < args.length; index++) {
270
- const value = this.emitExpression(args[index]!);
271
- const slot = this.reg();
272
- this.emit(` ${slot} = getelementptr i64, i64* ${ptr}, i32 ${index}`);
273
- this.emit(` store i64 ${value}, i64* ${slot}`);
266
+ emitArguments(args: readonly Expression[]): { argc: string; ptr: string } {
267
+ if (args.length === 0) return { argc: "0", ptr: "null" };
268
+ const hasSpread = args.some((element) => element.kind === SyntaxKind.SpreadElement);
269
+ if (!hasSpread) {
270
+ const ptr = `%args${this.current.allocas.length}`;
271
+ this.current.allocas.push(`${ptr} = alloca i64, i32 ${args.length}`);
272
+ for (let index = 0; index < args.length; index++) {
273
+ const value = this.emitExpression(args[index]!);
274
+ const slot = this.reg();
275
+ this.emit(` ${slot} = getelementptr i64, i64* ${ptr}, i32 ${index}`);
276
+ this.emit(` store i64 ${value}, i64* ${slot}`);
277
+ }
278
+ return { argc: String(args.length), ptr };
279
+ }
280
+ // At least one `...spread`: accumulate the argument list in a runtime
281
+ // array, which grows to whatever length the spread produces.
282
+ const array = this.reg();
283
+ this.emit(` ${array} = call i64 @xt_array_new(i32 0, i64* null)`);
284
+ for (const element of args) {
285
+ if (element.kind === SyntaxKind.SpreadElement) {
286
+ const value = this.emitExpression((element as SpreadElement).expression);
287
+ this.emit(` call i64 @xt_array_spread(i64 ${array}, i64 ${value})`);
288
+ } else {
289
+ const value = this.emitExpression(element);
290
+ this.emit(` call i64 @xt_array_push(i64 ${array}, i64 ${value})`);
291
+ }
274
292
  }
275
- return { argc: args.length, ptr };
293
+ const argc = this.reg();
294
+ this.emit(` ${argc} = call i32 @xt_array_size(i64 ${array})`);
295
+ const ptr = this.reg();
296
+ this.emit(` ${ptr} = call i64* @xt_array_items(i64 ${array})`);
297
+ return { argc, ptr };
276
298
  },
277
299
 
278
300
  emitDelete(node: DeleteExpression): string {
@@ -398,14 +420,15 @@ export const callMethods: CallMethods = {
398
420
  this.emit(` ${result} = call i64 @xt_array_new(i32 ${args.argc}, i64* ${args.ptr})`);
399
421
  return result;
400
422
  }
401
- let array = this.runtimeCall("xt_array_new", [`0`, `null`]);
423
+ const array = this.reg();
424
+ this.emit(` ${array} = call i64 @xt_array_new(i32 0, i64* null)`);
402
425
  for (const element of node.elements) {
403
426
  if (element.kind === SyntaxKind.SpreadElement) {
404
427
  const spread = this.emitExpression((element as { expression: Expression }).expression);
405
- array = this.runtimeCall("xt_array_spread", [array, spread]);
428
+ this.emit(` call i64 @xt_array_spread(i64 ${array}, i64 ${spread})`);
406
429
  } else {
407
430
  const value = this.emitExpression(element);
408
- array = this.runtimeCall("xt_array_push", [array, value]);
431
+ this.emit(` call i64 @xt_array_push(i64 ${array}, i64 ${value})`);
409
432
  }
410
433
  }
411
434
  return array;
@@ -416,8 +439,15 @@ export const callMethods: CallMethods = {
416
439
  this.emit(` ${object} = call i64 @xt_object_new()`);
417
440
  for (const property of node.properties) {
418
441
  if (property.kind === SyntaxKind.PropertyAssignment) {
419
- const name = propertyNameText(property.name);
420
- const key = this.stringValue(name);
442
+ const nameNode = property.name;
443
+ let key: string;
444
+ if (nameNode.kind === SyntaxKind.ComputedPropertyName) {
445
+ const keyValue = this.emitExpression((nameNode as { expression: Expression }).expression);
446
+ key = this.reg();
447
+ this.emit(` ${key} = call i64 @xt_to_string(i64 ${keyValue})`);
448
+ } else {
449
+ key = this.stringValue(propertyNameText(nameNode));
450
+ }
421
451
  const value = this.emitExpression(property.initializer);
422
452
  this.emit(` call i64 @xt_set(i64 ${object}, i64 ${key}, i64 ${value})`);
423
453
  } else if (property.kind === SyntaxKind.ShorthandPropertyAssignment) {
@@ -482,6 +512,19 @@ export const callMethods: CallMethods = {
482
512
 
483
513
  emitSuperConstructor(node: CallExpression): string {
484
514
  const thisValue = this.emitThis();
515
+ const classInfo = this.current.fn.classInfo;
516
+ const parent = classInfo?.parentExpression;
517
+ const unboundBuiltin =
518
+ parent !== undefined &&
519
+ parent.kind === SyntaxKind.Identifier &&
520
+ (parent as Identifier).text === "Error" &&
521
+ !this.binding.symbolOfIdentifier.get(parent as Identifier);
522
+ if (unboundBuiltin) {
523
+ const args = this.emitArguments(node.arguments);
524
+ const result = this.reg();
525
+ this.emit(` ${result} = call i64 @xt_error_init(i64 ${thisValue}, i32 ${args.argc}, i64* ${args.ptr})`);
526
+ return result;
527
+ }
485
528
  const proto = this.reg();
486
529
  this.emit(` ${proto} = call i64 @xt_object_get_prototype(i64 ${thisValue})`);
487
530
  const parentProto = this.reg();
@@ -117,7 +117,7 @@ export class GeneratorContext {
117
117
  return ptr;
118
118
  }
119
119
 
120
- const(hex: bigint): string {
120
+ const(hex: string): string {
121
121
  return i64(hex);
122
122
  }
123
123
 
@@ -33,7 +33,7 @@ import {
33
33
  import { SymbolKind, type SymbolInfo } from "../../binder/binder.js";
34
34
  import { DiagnosticCode } from "../../diagnostics/diagnostic.js";
35
35
  import { i64, numberLiteral, XT_FALSE, XT_NULL, XT_TRUE, XT_UNDEFINED } from "../values.js";
36
- import { BINARY_RUNTIME, compoundToBinary, isAssignmentOperator, CTOR_FUNCTIONS } from "./tables.js";
36
+ import { BINARY_RUNTIME, compoundToBinary, isAssignmentOperator, BUILTIN_FUNCTION_VALUES, CTOR_FUNCTIONS } from "./tables.js";
37
37
  import type { Generator } from "./generator.js";
38
38
 
39
39
  export interface ExpressionMethods {
@@ -64,11 +64,27 @@ export const expressionMethods: ExpressionMethods = {
64
64
  return numberLiteral(value);
65
65
  }
66
66
  case SyntaxKind.BigIntLiteral:
67
- return numberLiteral(Number((node as { value: bigint }).value));
67
+ return numberLiteral((node as { value: number }).value);
68
68
  case SyntaxKind.StringLiteral:
69
69
  return this.stringValue((node as { value: string }).value);
70
70
  case SyntaxKind.NoSubstitutionTemplateLiteral:
71
71
  return this.stringValue((node as { value: string }).value);
72
+ case SyntaxKind.RegularExpressionLiteral: {
73
+ const regex = node as { pattern: string; flags: string };
74
+ const pattern = this.stringValue(regex.pattern);
75
+ const flags = this.stringValue(regex.flags);
76
+ const ptr = `%regex${this.current.allocas.length}`;
77
+ this.current.allocas.push(`${ptr} = alloca i64, i32 2`);
78
+ const slot0 = this.reg();
79
+ this.emit(` ${slot0} = getelementptr i64, i64* ${ptr}, i32 0`);
80
+ this.emit(` store i64 ${pattern}, i64* ${slot0}`);
81
+ const slot1 = this.reg();
82
+ this.emit(` ${slot1} = getelementptr i64, i64* ${ptr}, i32 1`);
83
+ this.emit(` store i64 ${flags}, i64* ${slot1}`);
84
+ const result = this.reg();
85
+ this.emit(` ${result} = call i64 @xt_regexp_ctor(i32 2, i64* ${ptr})`);
86
+ return result;
87
+ }
72
88
  case SyntaxKind.TemplateLiteral:
73
89
  return this.emitTemplate(node as TemplateLiteral);
74
90
  case SyntaxKind.TrueKeyword:
@@ -87,6 +103,15 @@ export const expressionMethods: ExpressionMethods = {
87
103
  return this.emitExpression((node as { expression: Expression }).expression);
88
104
  case SyntaxKind.DeleteExpression:
89
105
  return this.emitDelete(node as DeleteExpression);
106
+ case SyntaxKind.TypeOfExpression: {
107
+ const operand = this.emitExpression((node as { expression: Expression }).expression);
108
+ const result = this.reg();
109
+ this.emit(` ${result} = call i64 @xt_typeof(i64 ${operand})`);
110
+ return result;
111
+ }
112
+ case SyntaxKind.VoidExpression:
113
+ this.emitExpression((node as { expression: Expression }).expression);
114
+ return i64(XT_UNDEFINED);
90
115
  case SyntaxKind.ThisKeyword:
91
116
  return this.emitThis();
92
117
  case SyntaxKind.NewExpression:
@@ -172,7 +197,17 @@ export const expressionMethods: ExpressionMethods = {
172
197
  this.emit(` ${rest} = call i64 @xt_rest_args(i32 ${argc}, i64* ${argv}, i32 0)`);
173
198
  return rest;
174
199
  }
175
- default:
200
+ default: {
201
+ const builtinValue = BUILTIN_FUNCTION_VALUES[identifier.text];
202
+ if (builtinValue) {
203
+ // A global like `Boolean` used as a value (`arr.filter(Boolean)`):
204
+ // wrap its runtime trampoline in a closure object.
205
+ const cast = this.reg();
206
+ this.emit(` ${cast} = bitcast i64 (i64, i64, i32, i64*)* @${builtinValue} to i8*`);
207
+ const closure = this.reg();
208
+ this.emit(` ${closure} = call i64 @xt_closure_new(i8* ${cast}, i32 0, i64* null)`);
209
+ return closure;
210
+ }
176
211
  this.diagnostics.error(
177
212
  DiagnosticCode.CannotFindName,
178
213
  `Cannot find name '${identifier.text}'`,
@@ -180,6 +215,7 @@ export const expressionMethods: ExpressionMethods = {
180
215
  this.sourceFile.fileName,
181
216
  );
182
217
  return i64(XT_UNDEFINED);
218
+ }
183
219
  }
184
220
  },
185
221
 
@@ -4,18 +4,32 @@
4
4
  */
5
5
 
6
6
  import {
7
+ ModifierKind,
7
8
  SyntaxKind,
8
9
  type ArrowFunction,
9
10
  type Block,
10
11
  type Expression,
12
+ type Identifier,
11
13
  type Parameter,
12
14
  } from "../../ast/nodes.js";
13
15
  import type { ClassInfo, FunctionInfo, SymbolInfo } from "../../binder/binder.js";
14
16
  import { SymbolKind } from "../../binder/binder.js";
15
17
  import { i64, XT_UNDEFINED } from "../values.js";
16
18
  import { RUNTIME_DECLARATIONS, type FunctionState } from "./state.js";
19
+ import { propertyNameText } from "./tables.js";
17
20
  import type { Generator } from "./generator.js";
18
21
 
22
+ /** Accessibility/`readonly` parameter modifiers make a constructor parameter a property. */
23
+ function isParameterProperty(parameter: Parameter): boolean {
24
+ return parameter.modifiers.some(
25
+ (modifier) =>
26
+ modifier.modifierKind === ModifierKind.Public ||
27
+ modifier.modifierKind === ModifierKind.Private ||
28
+ modifier.modifierKind === ModifierKind.Protected ||
29
+ modifier.modifierKind === ModifierKind.Readonly,
30
+ );
31
+ }
32
+
19
33
  export interface ModuleMethods {
20
34
  run(this: Generator): string;
21
35
  emitFunction(this: Generator, fn: FunctionInfo): void;
@@ -40,8 +54,11 @@ export const moduleMethods: ModuleMethods = {
40
54
  const moduleScope = this.binding.scopes.get(this.sourceFile);
41
55
  if (moduleScope) {
42
56
  for (const symbol of moduleScope.symbols.values()) {
43
- const isVariable = symbol.kind === SymbolKind.Var || symbol.kind === SymbolKind.Let || symbol.kind === SymbolKind.Const;
44
- if (!isVariable || !symbol.boxed || !symbol.fn.isModule) continue;
57
+ const isVariable =
58
+ symbol.kind === SymbolKind.Var || symbol.kind === SymbolKind.Let || symbol.kind === SymbolKind.Const;
59
+ const isEnum = symbol.kind === SymbolKind.Enum;
60
+ if ((!isVariable && !isEnum) || !symbol.fn?.isModule) continue;
61
+ if (isVariable && !symbol.boxed) continue;
45
62
  const globalName = `@g.${symbol.id}`;
46
63
  this.moduleGlobals.set(symbol.id, globalName);
47
64
  this.globals.push(`${globalName} = internal global i64 ${i64(XT_UNDEFINED)}`);
@@ -116,6 +133,31 @@ export const moduleMethods: ModuleMethods = {
116
133
  this.emit(` store i64 ${inherited}, i64* %saved.this`);
117
134
  }
118
135
 
136
+ // A derived class with no explicit constructor gets an implicit one that
137
+ // forwards every argument to the parent constructor (`super(...arguments)`).
138
+ if (fn.isConstructor && fn.classInfo?.parentExpression) {
139
+ const explicitBody = (fn.node as { body?: Block }).body;
140
+ const parent = fn.classInfo.parentExpression;
141
+ const unboundBuiltin =
142
+ parent.kind === SyntaxKind.Identifier &&
143
+ (parent as Identifier).text === "Error" &&
144
+ !this.binding.symbolOfIdentifier.get(parent as Identifier);
145
+ if (!explicitBody && !unboundBuiltin) {
146
+ const parentValue = this.emitExpression(parent);
147
+ const parentProto = this.reg();
148
+ this.emit(` ${parentProto} = call i64 @xt_function_get_prototype(i64 ${parentValue})`);
149
+ const ctorKey = this.stringValue("__ctor");
150
+ const parentCtor = this.reg();
151
+ this.emit(` ${parentCtor} = call i64 @xt_get(i64 ${parentProto}, i64 ${ctorKey})`);
152
+ const superThis = this.emitThis();
153
+ this.emit(` call i64 @xt_call_with_this(i64 ${parentCtor}, i64 ${superThis}, i32 %argc, i64* %argv)`);
154
+ } else if (!explicitBody && unboundBuiltin) {
155
+ // `extends Error` with no explicit constructor: initialise message/name.
156
+ const superThis = this.emitThis();
157
+ this.emit(` call i64 @xt_error_init(i64 ${superThis}, i32 %argc, i64* %argv)`);
158
+ }
159
+ }
160
+
119
161
  // Instance field initializers run before the constructor body.
120
162
  if (fn.isConstructor && fn.classInfo) {
121
163
  const thisValue = this.emitThis();
@@ -127,6 +169,20 @@ export const moduleMethods: ModuleMethods = {
127
169
  }
128
170
  }
129
171
 
172
+ // Constructor parameter properties (`constructor(private readonly x: T) {}`)
173
+ // copy the incoming argument onto `this`.
174
+ if (fn.isConstructor) {
175
+ const thisValue = this.emitThis();
176
+ for (let index = 0; index < fn.params.length; index++) {
177
+ const parameter = parameterNodes[index];
178
+ if (!parameter || !isParameterProperty(parameter)) continue;
179
+ const symbol = fn.params[index]!;
180
+ const key = this.stringValue(propertyNameText(parameter.name));
181
+ const value = this.readSlot(symbol);
182
+ this.emit(` call i64 @xt_set(i64 ${thisValue}, i64 ${key}, i64 ${value})`);
183
+ }
184
+ }
185
+
130
186
  // Body.
131
187
  if (fn.node.kind === SyntaxKind.SourceFile) {
132
188
  this.emitStatements(this.sourceFile.statements);
@@ -199,15 +255,31 @@ export const moduleMethods: ModuleMethods = {
199
255
  const proto = this.reg();
200
256
  this.emit(` ${proto} = call i64 @xt_object_new()`);
201
257
  if (classInfo.parentExpression) {
202
- const parent = this.emitExpression(classInfo.parentExpression);
203
- const parentProto = this.reg();
204
- this.emit(` ${parentProto} = call i64 @xt_function_get_prototype(i64 ${parent})`);
205
- this.emit(` call i64 @xt_object_set_prototype(i64 ${proto}, i64 ${parentProto})`);
258
+ // `extends Error` (and other host builtins) have no runtime class value;
259
+ // the subclass still works, it simply does not inherit a prototype.
260
+ const parent = classInfo.parentExpression;
261
+ const unboundBuiltin =
262
+ parent.kind === SyntaxKind.Identifier &&
263
+ (parent as Identifier).text === "Error" &&
264
+ !this.binding.symbolOfIdentifier.get(parent as Identifier);
265
+ if (!unboundBuiltin) {
266
+ const parentValue = this.emitExpression(parent);
267
+ const parentProto = this.reg();
268
+ this.emit(` ${parentProto} = call i64 @xt_function_get_prototype(i64 ${parentValue})`);
269
+ this.emit(` call i64 @xt_object_set_prototype(i64 ${proto}, i64 ${parentProto})`);
270
+ }
206
271
  }
207
272
  for (const method of classInfo.methods) {
208
273
  const fnValue = this.emitClosureValue(method);
209
274
  const key = this.stringValue(method.name);
210
- this.emit(` call i64 @xt_set(i64 ${proto}, i64 ${key}, i64 ${fnValue})`);
275
+ const accessor = (method.node as { accessor?: "get" | "set" }).accessor;
276
+ if (accessor === "get") {
277
+ this.emit(` call i64 @xt_object_define_getter(i64 ${proto}, i64 ${key}, i64 ${fnValue})`);
278
+ } else if (accessor === "set") {
279
+ this.emit(` call i64 @xt_object_define_setter(i64 ${proto}, i64 ${key}, i64 ${fnValue})`);
280
+ } else {
281
+ this.emit(` call i64 @xt_set(i64 ${proto}, i64 ${key}, i64 ${fnValue})`);
282
+ }
211
283
  }
212
284
  const ctor = this.emitClosureValue(classInfo.ctor!);
213
285
  this.emit(` call i64 @xt_function_set_prototype(i64 ${ctor}, i64 ${proto})`);
@@ -68,6 +68,8 @@ export const RUNTIME_DECLARATIONS: readonly string[] = [
68
68
  "declare i64 @xt_object_new_with_proto(i64)",
69
69
  "declare i64 @xt_object_get_prototype(i64)",
70
70
  "declare i64 @xt_object_set_prototype(i64, i64)",
71
+ "declare i64 @xt_object_define_getter(i64, i64, i64)",
72
+ "declare i64 @xt_object_define_setter(i64, i64, i64)",
71
73
  "declare i64 @xt_await(i64)",
72
74
  "declare void @xt_drain_microtasks()",
73
75
  "declare void @xt_run_event_loop()",
@@ -101,6 +103,8 @@ export const RUNTIME_DECLARATIONS: readonly string[] = [
101
103
  "declare i64 @xt_set_ctor(i32, i64*)",
102
104
  "declare i64 @xt_date_ctor(i32, i64*)",
103
105
  "declare i64 @xt_regexp_ctor(i32, i64*)",
106
+ "declare i64 @xt_error_ctor(i32, i64*)",
107
+ "declare i64 @xt_error_init(i64, i32, i64*)",
104
108
  "declare i64 @xt_json_parse(i32, i64*)",
105
109
  "declare i64 @xt_json_stringify(i32, i64*)",
106
110
  "declare i64 @xt_object_from_entries(i64)",
@@ -168,10 +172,22 @@ export const RUNTIME_DECLARATIONS: readonly string[] = [
168
172
  "declare i64 @xt_delete(i64, i64)",
169
173
  "declare i64 @xt_rest_args(i32, i64*, i32)",
170
174
  "declare void @xt_set_program_args(i32, i8**)",
175
+ "declare i64 @xt_import_meta(i64)",
171
176
  "declare i64 @xt_array_new(i32, i64*)",
172
177
  "declare i64 @xt_array_push(i64, i64)",
173
178
  "declare i64 @xt_array_length(i64)",
174
179
  "declare i64 @xt_array_spread(i64, i64)",
180
+ "declare i64 @xt_iter_length(i64)",
181
+ "declare i64 @xt_iter_value(i64, i64)",
182
+ "declare i32 @xt_array_size(i64)",
183
+ "declare i64* @xt_array_items(i64)",
184
+ "declare i64 @xt_builtin_value_parseInt(i64, i64, i32, i64*)",
185
+ "declare i64 @xt_builtin_value_parseFloat(i64, i64, i32, i64*)",
186
+ "declare i64 @xt_builtin_value_isNaN(i64, i64, i32, i64*)",
187
+ "declare i64 @xt_builtin_value_isFinite(i64, i64, i32, i64*)",
188
+ "declare i64 @xt_builtin_value_number(i64, i64, i32, i64*)",
189
+ "declare i64 @xt_builtin_value_string(i64, i64, i32, i64*)",
190
+ "declare i64 @xt_builtin_value_boolean(i64, i64, i32, i64*)",
175
191
  "declare i64 @xt_get(i64, i64)",
176
192
  "declare i64 @xt_set(i64, i64, i64)",
177
193
  "declare i64 @xt_box_new(i64)",