xbintsc 0.1.6 → 0.1.8

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 (137) hide show
  1. package/README.md +7 -4
  2. package/dist/src/ast/common.d.ts +33 -0
  3. package/dist/src/ast/common.js +7 -0
  4. package/dist/src/ast/common.js.map +1 -0
  5. package/dist/src/ast/declarations.d.ts +110 -0
  6. package/dist/src/ast/declarations.js +5 -0
  7. package/dist/src/ast/declarations.js.map +1 -0
  8. package/dist/src/ast/expressions.d.ts +219 -0
  9. package/dist/src/ast/expressions.js +6 -0
  10. package/dist/src/ast/expressions.js.map +1 -0
  11. package/dist/src/ast/kinds.d.ts +160 -0
  12. package/dist/src/ast/kinds.js +171 -0
  13. package/dist/src/ast/kinds.js.map +1 -0
  14. package/dist/src/ast/modules.d.ts +61 -0
  15. package/dist/src/ast/modules.js +5 -0
  16. package/dist/src/ast/modules.js.map +1 -0
  17. package/dist/src/ast/nodes.d.ts +11 -839
  18. package/dist/src/ast/nodes.js +11 -237
  19. package/dist/src/ast/nodes.js.map +1 -1
  20. package/dist/src/ast/operators.d.ts +71 -0
  21. package/dist/src/ast/operators.js +77 -0
  22. package/dist/src/ast/operators.js.map +1 -0
  23. package/dist/src/ast/statements.d.ts +120 -0
  24. package/dist/src/ast/statements.js +5 -0
  25. package/dist/src/ast/statements.js.map +1 -0
  26. package/dist/src/ast/types.d.ts +123 -0
  27. package/dist/src/ast/types.js +6 -0
  28. package/dist/src/ast/types.js.map +1 -0
  29. package/dist/src/codegen/generator/calls.d.ts +22 -0
  30. package/dist/src/codegen/generator/calls.js +301 -0
  31. package/dist/src/codegen/generator/calls.js.map +1 -0
  32. package/dist/src/codegen/generator/context.d.ts +45 -0
  33. package/dist/src/codegen/generator/context.js +128 -0
  34. package/dist/src/codegen/generator/context.js.map +1 -0
  35. package/dist/src/codegen/generator/expressions.d.ts +22 -0
  36. package/dist/src/codegen/generator/expressions.js +383 -0
  37. package/dist/src/codegen/generator/expressions.js.map +1 -0
  38. package/dist/src/codegen/generator/generator.d.ts +22 -0
  39. package/dist/src/codegen/generator/generator.js +21 -0
  40. package/dist/src/codegen/generator/generator.js.map +1 -0
  41. package/dist/src/codegen/generator/module.d.ts +15 -0
  42. package/dist/src/codegen/generator/module.js +114 -0
  43. package/dist/src/codegen/generator/module.js.map +1 -0
  44. package/dist/src/codegen/generator/state.d.ts +40 -0
  45. package/dist/src/codegen/generator/state.js +84 -0
  46. package/dist/src/codegen/generator/state.js.map +1 -0
  47. package/dist/src/codegen/generator/statements.d.ts +22 -0
  48. package/dist/src/codegen/generator/statements.js +425 -0
  49. package/dist/src/codegen/generator/statements.js.map +1 -0
  50. package/dist/src/codegen/generator/tables.d.ts +19 -0
  51. package/dist/src/codegen/generator/tables.js +153 -0
  52. package/dist/src/codegen/generator/tables.js.map +1 -0
  53. package/dist/src/codegen/llvm.d.ts +4 -13
  54. package/dist/src/codegen/llvm.js +4 -1537
  55. package/dist/src/codegen/llvm.js.map +1 -1
  56. package/dist/src/driver/compiler.js +13 -4
  57. package/dist/src/driver/compiler.js.map +1 -1
  58. package/dist/src/lexer/char.d.ts +78 -0
  59. package/dist/src/lexer/char.js +106 -0
  60. package/dist/src/lexer/char.js.map +1 -0
  61. package/dist/src/lexer/scanner.js +1 -98
  62. package/dist/src/lexer/scanner.js.map +1 -1
  63. package/dist/src/parser/context.d.ts +58 -0
  64. package/dist/src/parser/context.js +174 -0
  65. package/dist/src/parser/context.js.map +1 -0
  66. package/dist/src/parser/declarations.d.ts +26 -0
  67. package/dist/src/parser/declarations.js +461 -0
  68. package/dist/src/parser/declarations.js.map +1 -0
  69. package/dist/src/parser/expressions.d.ts +27 -0
  70. package/dist/src/parser/expressions.js +437 -0
  71. package/dist/src/parser/expressions.js.map +1 -0
  72. package/dist/src/parser/helpers.d.ts +7 -0
  73. package/dist/src/parser/helpers.js +36 -0
  74. package/dist/src/parser/helpers.js.map +1 -0
  75. package/dist/src/parser/literals.d.ts +16 -0
  76. package/dist/src/parser/literals.js +193 -0
  77. package/dist/src/parser/literals.js.map +1 -0
  78. package/dist/src/parser/modules.d.ts +12 -0
  79. package/dist/src/parser/modules.js +175 -0
  80. package/dist/src/parser/modules.js.map +1 -0
  81. package/dist/src/parser/operators.d.ts +12 -0
  82. package/dist/src/parser/operators.js +172 -0
  83. package/dist/src/parser/operators.js.map +1 -0
  84. package/dist/src/parser/parser.d.ts +17 -122
  85. package/dist/src/parser/parser.js +18 -2311
  86. package/dist/src/parser/parser.js.map +1 -1
  87. package/dist/src/parser/speculation.d.ts +6 -0
  88. package/dist/src/parser/speculation.js +10 -0
  89. package/dist/src/parser/speculation.js.map +1 -0
  90. package/dist/src/parser/statements.d.ts +28 -0
  91. package/dist/src/parser/statements.js +402 -0
  92. package/dist/src/parser/statements.js.map +1 -0
  93. package/dist/src/parser/types.d.ts +21 -0
  94. package/dist/src/parser/types.js +338 -0
  95. package/dist/src/parser/types.js.map +1 -0
  96. package/package.json +1 -1
  97. package/runtime/rt_internal.h +106 -0
  98. package/runtime/xt_alloc.c +38 -0
  99. package/runtime/xt_builtins.c +192 -0
  100. package/runtime/xt_containers.c +225 -0
  101. package/runtime/xt_io.c +133 -0
  102. package/runtime/xt_stdlib.c +502 -0
  103. package/runtime/xt_values.c +291 -0
  104. package/scripts/build-runtime.mjs +8 -1
  105. package/src/ast/common.ts +210 -0
  106. package/src/ast/declarations.ts +130 -0
  107. package/src/ast/expressions.ts +275 -0
  108. package/src/ast/kinds.ts +176 -0
  109. package/src/ast/modules.ts +73 -0
  110. package/src/ast/nodes.ts +11 -1101
  111. package/src/ast/operators.ts +76 -0
  112. package/src/ast/statements.ts +148 -0
  113. package/src/ast/types.ts +173 -0
  114. package/src/codegen/generator/calls.ts +355 -0
  115. package/src/codegen/generator/context.ts +156 -0
  116. package/src/codegen/generator/expressions.ts +431 -0
  117. package/src/codegen/generator/generator.ts +35 -0
  118. package/src/codegen/generator/module.ts +139 -0
  119. package/src/codegen/generator/state.ts +126 -0
  120. package/src/codegen/generator/statements.ts +474 -0
  121. package/src/codegen/generator/tables.ts +171 -0
  122. package/src/codegen/llvm.ts +4 -1720
  123. package/src/driver/compiler.ts +17 -5
  124. package/src/lexer/char.ts +116 -0
  125. package/src/lexer/scanner.ts +9 -109
  126. package/src/parser/context.ts +196 -0
  127. package/src/parser/declarations.ts +502 -0
  128. package/src/parser/expressions.ts +465 -0
  129. package/src/parser/helpers.ts +42 -0
  130. package/src/parser/literals.ts +207 -0
  131. package/src/parser/modules.ts +194 -0
  132. package/src/parser/operators.ts +181 -0
  133. package/src/parser/parser.ts +35 -2373
  134. package/src/parser/speculation.ts +9 -0
  135. package/src/parser/statements.ts +457 -0
  136. package/src/parser/types.ts +365 -0
  137. package/runtime/xt_runtime.c +0 -1372
@@ -15,1542 +15,9 @@
15
15
  *
16
16
  * Later pipeline stages (optimization passes, object emission) live in the
17
17
  * driver; this module only produces IR text.
18
+ *
19
+ * The implementation is split by function under `./generator/`; this module is
20
+ * the stable entry point.
18
21
  */
19
- import { SyntaxKind, } from "../ast/nodes.js";
20
- import { AssignmentOperator, BinaryOperator, PrefixUnaryOperator, PostfixUnaryOperator, } from "../ast/nodes.js";
21
- import { bind, SymbolKind, } from "../binder/binder.js";
22
- import { DiagnosticCode } from "../diagnostics/diagnostic.js";
23
- import { i64, numberLiteral, XT_UNDEFINED, XT_NULL, XT_FALSE, XT_TRUE } from "./values.js";
24
- const RUNTIME_DECLARATIONS = [
25
- "declare i64 @xt_undefined()",
26
- "declare i64 @xt_null()",
27
- "declare i64 @xt_bool(i32)",
28
- "declare i64 @xt_number(double)",
29
- "declare i64 @xt_string_new(i8*, i64)",
30
- "declare i64 @xt_string_from_cstr(i8*)",
31
- "declare i64 @xt_arg(i32, i64*, i32)",
32
- "declare i64 @xt_closure_new(i8*, i32, i64*)",
33
- "declare i64 @xt_closure_call(i64, i32, i64*)",
34
- "declare i64 @xt_closure_env(i64, i32)",
35
- "declare i32 @xt_truthy(i64)",
36
- "declare double @xt_to_number(i64)",
37
- "declare i64 @xt_to_string(i64)",
38
- "declare i64 @xt_typeof(i64)",
39
- "declare i64 @xt_add(i64, i64)",
40
- "declare i64 @xt_sub(i64, i64)",
41
- "declare i64 @xt_mul(i64, i64)",
42
- "declare i64 @xt_div(i64, i64)",
43
- "declare i64 @xt_mod(i64, i64)",
44
- "declare i64 @xt_pow(i64, i64)",
45
- "declare i64 @xt_neg(i64)",
46
- "declare i64 @xt_pos(i64)",
47
- "declare i64 @xt_bit_and(i64, i64)",
48
- "declare i64 @xt_bit_or(i64, i64)",
49
- "declare i64 @xt_bit_xor(i64, i64)",
50
- "declare i64 @xt_bit_not(i64)",
51
- "declare i64 @xt_shl(i64, i64)",
52
- "declare i64 @xt_shr(i64, i64)",
53
- "declare i64 @xt_ushr(i64, i64)",
54
- "declare i64 @xt_lt(i64, i64)",
55
- "declare i64 @xt_le(i64, i64)",
56
- "declare i64 @xt_gt(i64, i64)",
57
- "declare i64 @xt_ge(i64, i64)",
58
- "declare i64 @xt_eq(i64, i64)",
59
- "declare i64 @xt_ne(i64, i64)",
60
- "declare i64 @xt_seq(i64, i64)",
61
- "declare i64 @xt_sne(i64, i64)",
62
- "declare i64 @xt_not(i64)",
63
- "declare i64 @xt_object_new()",
64
- "declare i64 @xt_object_get_cstr(i64, i8*)",
65
- "declare i64 @xt_object_set(i64, i64, i64)",
66
- "declare i64 @xt_object_has(i64, i64)",
67
- "declare i64 @xt_object_keys(i64)",
68
- "declare i64 @xt_object_values(i64)",
69
- "declare i64 @xt_object_entries(i64)",
70
- "declare i64 @xt_object_assign(i32, i64*)",
71
- "declare i64 @xt_object_spread(i64, i64)",
72
- "declare i64 @xt_call_method(i64, i64, i32, i64*)",
73
- "declare i64 @xt_math_call(i64, i32, i64*)",
74
- "declare i64 @xt_parse_int(i32, i64*)",
75
- "declare i64 @xt_parse_float(i32, i64*)",
76
- "declare i64 @xt_is_nan(i32, i64*)",
77
- "declare i64 @xt_is_finite(i32, i64*)",
78
- "declare i64 @xt_number_ctor(i32, i64*)",
79
- "declare i64 @xt_string_ctor(i32, i64*)",
80
- "declare i64 @xt_boolean_ctor(i32, i64*)",
81
- "declare i64 @xt_in(i64, i64)",
82
- "declare i64 @xt_delete(i64, i64)",
83
- "declare i64 @xt_rest_args(i32, i64*, i32)",
84
- "declare i64 @xt_array_new(i32, i64*)",
85
- "declare i64 @xt_array_push(i64, i64)",
86
- "declare i64 @xt_array_length(i64)",
87
- "declare i64 @xt_array_spread(i64, i64)",
88
- "declare i64 @xt_get(i64, i64)",
89
- "declare i64 @xt_set(i64, i64, i64)",
90
- "declare i64 @xt_box_new(i64)",
91
- "declare i64 @xt_box_get(i64)",
92
- "declare i64 @xt_box_set(i64, i64)",
93
- "declare i64 @xt_is_nullish(i64)",
94
- "declare void @xt_throw(i64)",
95
- "declare i32 @_setjmp(i8*) returns_twice",
96
- "declare i8* @xt_try_enter()",
97
- "declare i64 @xt_try_exception(i8*)",
98
- "declare void @xt_try_leave(i8*)",
99
- "declare void @xt_console_log(i32, i64*)",
100
- "declare void @xt_console_info(i32, i64*)",
101
- "declare void @xt_console_warn(i32, i64*)",
102
- "declare void @xt_console_error(i32, i64*)",
103
- ];
104
- export function generate(sourceFile, diagnostics, options = {}) {
105
- const generator = new Generator(sourceFile, diagnostics, options);
106
- return { ir: generator.run(), binding: generator.binding };
107
- }
108
- class Generator {
109
- sourceFile;
110
- diagnostics;
111
- binding;
112
- globals = [];
113
- functions = [];
114
- strings = new Map();
115
- extraDeclarations = new Set();
116
- builtins;
117
- stringCounter = 0;
118
- current;
119
- constructor(sourceFile, diagnostics, options = {}) {
120
- this.sourceFile = sourceFile;
121
- this.diagnostics = diagnostics;
122
- this.binding = bind(sourceFile);
123
- this.builtins = options.builtins ?? {};
124
- }
125
- run() {
126
- for (const fn of this.binding.functions)
127
- this.emitFunction(fn);
128
- this.emitMain();
129
- const header = ["; ModuleID = 'xbintsc'", "source_filename = \"" + this.sourceFile.fileName + "\"", ""];
130
- return [...header, ...RUNTIME_DECLARATIONS, ...this.extraDeclarations, "", ...this.globals, "", ...this.functions, ""].join("\n");
131
- }
132
- // -- module level --------------------------------------------------------
133
- emitFunction(fn) {
134
- const name = this.functionName(fn);
135
- const state = {
136
- fn,
137
- buffer: [],
138
- allocas: [],
139
- slots: new Map(),
140
- reg: 0,
141
- label: 0,
142
- terminated: false,
143
- loops: [],
144
- tryFrames: [],
145
- escapePointers: [],
146
- usesTry: false,
147
- };
148
- this.current = state;
149
- const header = `define i64 @${name}(i64 %env, i32 %argc, i64* %argv) {`;
150
- this.emit(`%saved.env = alloca i64`);
151
- this.emit(`store i64 %env, i64* %saved.env`);
152
- this.emit(`%saved.argc = alloca i32`);
153
- this.emit(`store i32 %argc, i32* %saved.argc`);
154
- this.emit(`%saved.argv = alloca i64*`);
155
- this.emit(`store i64* %argv, i64** %saved.argv`);
156
- // Parameters.
157
- const parameterNodes = fn.node.parameters ?? [];
158
- for (let index = 0; index < fn.params.length; index++) {
159
- const symbol = fn.params[index];
160
- const parameter = parameterNodes[index];
161
- if (parameter?.dotDotDotToken) {
162
- const rest = this.reg();
163
- this.emit(` ${rest} = call i64 @xt_rest_args(i32 %argc, i64* %argv, i32 ${index})`);
164
- this.declareSlot(symbol, rest);
165
- continue;
166
- }
167
- const value = this.reg();
168
- this.emit(` ${value} = call i64 @xt_arg(i32 %argc, i64* %argv, i32 ${index})`);
169
- this.declareSlot(symbol, value);
170
- if (parameter?.initializer)
171
- this.emitDefaultParameter(symbol, value, parameter.initializer);
172
- }
173
- // Captures threaded through the environment.
174
- for (const symbol of fn.captures) {
175
- const index = fn.captureIndex.get(symbol.id);
176
- const value = this.reg();
177
- this.emit(` ${value} = call i64 @xt_closure_env(i64 %env, i32 ${index})`);
178
- this.defineCaptureSlot(symbol, value);
179
- }
180
- // Body.
181
- if (fn.node.kind === SyntaxKind.SourceFile) {
182
- this.emitStatements(this.sourceFile.statements);
183
- }
184
- else if (fn.node.kind === SyntaxKind.ArrowFunction && fn.node.body.kind !== SyntaxKind.Block) {
185
- const value = this.emitExpression(fn.node.body);
186
- this.terminate(`ret i64 ${value}`);
187
- }
188
- else {
189
- const body = fn.node.body;
190
- this.emitStatements(body.statements);
191
- }
192
- if (!this.current.terminated)
193
- this.terminate(`ret i64 ${i64(XT_UNDEFINED)}`);
194
- const escapes = state.usesTry
195
- ? state.escapePointers.map((ptr) => ` call void asm sideeffect "", "r"(i64* ${ptr})`)
196
- : [];
197
- const lines = [...state.allocas.map((a) => ` ${a}`), ...escapes, ...state.buffer];
198
- this.functions.push([header, ...lines, "}", ""].join("\n"));
199
- }
200
- emitMain() {
201
- const moduleName = this.functionName(this.binding.moduleFunction);
202
- this.functions.push([
203
- "define i32 @main(i32 %argc, i8** %argv) {",
204
- ` %result = call i64 @${moduleName}(i64 ${i64(XT_UNDEFINED)}, i32 0, i64* null)`,
205
- " ret i32 0",
206
- "}",
207
- "",
208
- ].join("\n"));
209
- }
210
- functionName(fn) {
211
- return fn.isModule ? "xt_module" : `xt_fn_${fn.id}`;
212
- }
213
- // -- emission primitives -------------------------------------------------
214
- emit(line) {
215
- this.current.buffer.push(line);
216
- }
217
- terminate(line) {
218
- this.current.buffer.push(` ${line}`);
219
- this.current.terminated = true;
220
- }
221
- reg() {
222
- return `%r${this.current.reg++}`;
223
- }
224
- label(prefix) {
225
- return `${prefix}.${this.current.label++}`;
226
- }
227
- startBlock(label) {
228
- this.current.buffer.push(`${label}:`);
229
- this.current.terminated = false;
230
- }
231
- alloca() {
232
- const ptr = `%slot${this.current.allocas.length}`;
233
- this.current.allocas.push(`${ptr} = alloca i64`);
234
- this.current.escapePointers.push(ptr);
235
- return ptr;
236
- }
237
- const(hex) {
238
- return i64(hex);
239
- }
240
- // -- variables -----------------------------------------------------------
241
- declareSlot(symbol, initial) {
242
- const ptr = this.alloca();
243
- if (symbol.boxed) {
244
- const box = this.reg();
245
- this.emit(` ${box} = call i64 @xt_box_new(i64 ${initial})`);
246
- this.emit(` store i64 ${box}, i64* ${ptr}`);
247
- }
248
- else {
249
- this.emit(` store i64 ${initial}, i64* ${ptr}`);
250
- }
251
- this.current.slots.set(symbol.id, { ptr, boxed: symbol.boxed });
252
- }
253
- defineCaptureSlot(symbol, box) {
254
- const ptr = this.alloca();
255
- this.emit(` store i64 ${box}, i64* ${ptr}`);
256
- // A captured symbol is always boxed; the environment holds the box.
257
- this.current.slots.set(symbol.id, { ptr, boxed: true });
258
- }
259
- readSlot(symbol) {
260
- const slot = this.current.slots.get(symbol.id);
261
- if (!slot) {
262
- // Referenced from an inner function without a capture slot: this should
263
- // be impossible once the binder has run, but stay defensive.
264
- this.diagnostics.error(DiagnosticCode.CodegenError, `Internal: no slot for '${symbol.name}' in ${this.current.fn.name}`);
265
- return i64(XT_UNDEFINED);
266
- }
267
- const value = this.reg();
268
- this.emit(` ${value} = load i64, i64* ${slot.ptr}`);
269
- if (!slot.boxed)
270
- return value;
271
- const unboxed = this.reg();
272
- this.emit(` ${unboxed} = call i64 @xt_box_get(i64 ${value})`);
273
- return unboxed;
274
- }
275
- writeSlot(symbol, value) {
276
- const slot = this.current.slots.get(symbol.id);
277
- if (!slot) {
278
- this.diagnostics.error(DiagnosticCode.CodegenError, `Internal: no slot for '${symbol.name}'`);
279
- return;
280
- }
281
- if (!slot.boxed) {
282
- this.emit(` store i64 ${value}, i64* ${slot.ptr}`);
283
- return;
284
- }
285
- const box = this.reg();
286
- this.emit(` ${box} = load i64, i64* ${slot.ptr}`);
287
- this.emit(` call i64 @xt_box_set(i64 ${box}, i64 ${value})`);
288
- }
289
- emitDefaultParameter(symbol, value, initializer) {
290
- const isUndefined = this.reg();
291
- this.emit(` ${isUndefined} = call i64 @xt_seq(i64 ${value}, i64 ${i64(XT_UNDEFINED)})`);
292
- const truthy = this.reg();
293
- this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${isUndefined})`);
294
- const condition = this.reg();
295
- this.emit(` ${condition} = icmp ne i32 ${truthy}, 0`);
296
- const applyLabel = this.label("param.default");
297
- const endLabel = this.label("param.end");
298
- this.terminate(`br i1 ${condition}, label %${applyLabel}, label %${endLabel}`);
299
- this.startBlock(applyLabel);
300
- const fallback = this.emitExpression(initializer);
301
- this.writeSlot(symbol, fallback);
302
- this.terminate(`br label %${endLabel}`);
303
- this.startBlock(endLabel);
304
- }
305
- // -- statements ----------------------------------------------------------
306
- emitStatements(statements) {
307
- for (const statement of statements) {
308
- if (this.current.terminated)
309
- break;
310
- this.emitStatement(statement);
311
- }
312
- }
313
- emitStatement(statement) {
314
- switch (statement.kind) {
315
- case SyntaxKind.ExpressionStatement:
316
- this.emitExpression(statement.expression);
317
- return;
318
- case SyntaxKind.VariableStatement:
319
- this.emitVariableStatement(statement);
320
- return;
321
- case SyntaxKind.Block:
322
- this.emitStatements(statement.statements);
323
- return;
324
- case SyntaxKind.EmptyStatement:
325
- case SyntaxKind.DebuggerStatement:
326
- case SyntaxKind.FunctionDeclaration:
327
- case SyntaxKind.InterfaceDeclaration:
328
- case SyntaxKind.TypeAliasDeclaration:
329
- return; // functions are emitted at module scope
330
- case SyntaxKind.IfStatement:
331
- this.emitIf(statement);
332
- return;
333
- case SyntaxKind.WhileStatement:
334
- this.emitWhile(statement);
335
- return;
336
- case SyntaxKind.DoStatement:
337
- this.emitDo(statement);
338
- return;
339
- case SyntaxKind.ForStatement:
340
- this.emitFor(statement);
341
- return;
342
- case SyntaxKind.ForOfStatement:
343
- case SyntaxKind.ForInStatement:
344
- this.emitForOf(statement);
345
- return;
346
- case SyntaxKind.SwitchStatement:
347
- this.emitSwitch(statement);
348
- return;
349
- case SyntaxKind.TryStatement:
350
- this.emitTry(statement);
351
- return;
352
- case SyntaxKind.ReturnStatement:
353
- this.emitReturn(statement);
354
- return;
355
- case SyntaxKind.BreakStatement: {
356
- const loop = this.current.loops[this.current.loops.length - 1];
357
- if (loop) {
358
- this.popTryFramesTo(loop.tryDepth ?? 0);
359
- this.terminate(`br label %${loop.breakLabel}`);
360
- }
361
- return;
362
- }
363
- case SyntaxKind.ContinueStatement: {
364
- const loop = this.current.loops[this.current.loops.length - 1];
365
- if (loop) {
366
- this.popTryFramesTo(loop.tryDepth ?? 0);
367
- this.terminate(`br label %${loop.continueLabel}`);
368
- }
369
- return;
370
- }
371
- case SyntaxKind.ThrowStatement: {
372
- const value = this.emitExpression(statement.expression);
373
- this.emit(` call void @xt_throw(i64 ${value})`);
374
- this.terminate("unreachable");
375
- return;
376
- }
377
- case SyntaxKind.ExportDeclaration: {
378
- const declaration = statement.declaration;
379
- if (declaration)
380
- this.emitStatement(declaration);
381
- return;
382
- }
383
- default:
384
- this.unsupported(statement, "statement");
385
- }
386
- }
387
- emitVariableStatement(statement) {
388
- for (const declaration of statement.declarationList.declarations) {
389
- const symbol = this.binding.symbolOfDeclaration.get(declaration);
390
- if (!symbol)
391
- continue;
392
- if (this.current.slots.has(symbol.id))
393
- continue; // hoisted `var`
394
- const initial = declaration.initializer
395
- ? this.emitExpression(declaration.initializer)
396
- : i64(XT_UNDEFINED);
397
- this.declareSlot(symbol, initial);
398
- }
399
- }
400
- emitIf(statement) {
401
- const thenLabel = this.label("if.then");
402
- const elseLabel = statement.elseStatement ? this.label("if.else") : undefined;
403
- const endLabel = this.label("if.end");
404
- const condition = this.emitExpression(statement.condition);
405
- const truthy = this.reg();
406
- this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${condition})`);
407
- const nonzero = this.reg();
408
- this.emit(` ${nonzero} = icmp ne i32 ${truthy}, 0`);
409
- this.terminate(`br i1 ${nonzero}, label %${thenLabel}, label %${elseLabel ?? endLabel}`);
410
- this.startBlock(thenLabel);
411
- this.emitStatement(statement.thenStatement);
412
- if (!this.current.terminated)
413
- this.terminate(`br label %${endLabel}`);
414
- if (elseLabel) {
415
- this.startBlock(elseLabel);
416
- this.emitStatement(statement.elseStatement);
417
- if (!this.current.terminated)
418
- this.terminate(`br label %${endLabel}`);
419
- }
420
- this.startBlock(endLabel);
421
- }
422
- emitWhile(statement) {
423
- const condLabel = this.label("while.cond");
424
- const bodyLabel = this.label("while.body");
425
- const endLabel = this.label("while.end");
426
- this.terminate(`br label %${condLabel}`);
427
- this.startBlock(condLabel);
428
- const condition = this.emitExpression(statement.condition);
429
- const truthy = this.reg();
430
- this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${condition})`);
431
- const nonzero = this.reg();
432
- this.emit(` ${nonzero} = icmp ne i32 ${truthy}, 0`);
433
- this.terminate(`br i1 ${nonzero}, label %${bodyLabel}, label %${endLabel}`);
434
- this.startBlock(bodyLabel);
435
- this.current.loops.push({ breakLabel: endLabel, continueLabel: condLabel, tryDepth: this.current.tryFrames.length });
436
- this.emitStatement(statement.statement);
437
- this.current.loops.pop();
438
- if (!this.current.terminated)
439
- this.terminate(`br label %${condLabel}`);
440
- this.startBlock(endLabel);
441
- }
442
- emitDo(statement) {
443
- const bodyLabel = this.label("do.body");
444
- const condLabel = this.label("do.cond");
445
- const endLabel = this.label("do.end");
446
- this.terminate(`br label %${bodyLabel}`);
447
- this.startBlock(bodyLabel);
448
- this.current.loops.push({ breakLabel: endLabel, continueLabel: condLabel, tryDepth: this.current.tryFrames.length });
449
- this.emitStatement(statement.statement);
450
- this.current.loops.pop();
451
- if (!this.current.terminated)
452
- this.terminate(`br label %${condLabel}`);
453
- this.startBlock(condLabel);
454
- const condition = this.emitExpression(statement.condition);
455
- const truthy = this.reg();
456
- this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${condition})`);
457
- const nonzero = this.reg();
458
- this.emit(` ${nonzero} = icmp ne i32 ${truthy}, 0`);
459
- this.terminate(`br i1 ${nonzero}, label %${bodyLabel}, label %${endLabel}`);
460
- this.startBlock(endLabel);
461
- }
462
- emitFor(statement) {
463
- if (statement.initializer && statement.initializer.kind === SyntaxKind.VariableDeclarationList) {
464
- this.emitVariableStatement({
465
- kind: SyntaxKind.VariableStatement,
466
- declarationList: statement.initializer,
467
- modifiers: [],
468
- start: statement.initializer.start,
469
- end: statement.initializer.end,
470
- });
471
- }
472
- else if (statement.initializer) {
473
- this.emitExpression(statement.initializer);
474
- }
475
- const condLabel = this.label("for.cond");
476
- const bodyLabel = this.label("for.body");
477
- const updateLabel = this.label("for.update");
478
- const endLabel = this.label("for.end");
479
- this.terminate(`br label %${condLabel}`);
480
- this.startBlock(condLabel);
481
- if (statement.condition) {
482
- const condition = this.emitExpression(statement.condition);
483
- const truthy = this.reg();
484
- this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${condition})`);
485
- const nonzero = this.reg();
486
- this.emit(` ${nonzero} = icmp ne i32 ${truthy}, 0`);
487
- this.terminate(`br i1 ${nonzero}, label %${bodyLabel}, label %${endLabel}`);
488
- }
489
- else {
490
- this.terminate(`br label %${bodyLabel}`);
491
- }
492
- this.startBlock(bodyLabel);
493
- this.current.loops.push({ breakLabel: endLabel, continueLabel: updateLabel, tryDepth: this.current.tryFrames.length });
494
- this.emitStatement(statement.statement);
495
- this.current.loops.pop();
496
- if (!this.current.terminated)
497
- this.terminate(`br label %${updateLabel}`);
498
- this.startBlock(updateLabel);
499
- if (statement.incrementor)
500
- this.emitExpression(statement.incrementor);
501
- this.terminate(`br label %${condLabel}`);
502
- this.startBlock(endLabel);
503
- }
504
- /** `for (const x of xs)` / `for (const k in obj)` over arrays, strings and objects. */
505
- emitForOf(statement) {
506
- const source = this.emitExpression(statement.expression);
507
- const isForIn = statement.kind === SyntaxKind.ForInStatement;
508
- // `for...in` iterates the enumerable keys (indices become strings);
509
- // `for...of` iterates the values at each index.
510
- const iterable = isForIn ? this.runtimeCall("xt_object_keys", [source]) : source;
511
- const indexPtr = this.alloca();
512
- this.emit(` store i64 ${numberLiteral(0)}, i64* ${indexPtr}`);
513
- const lengthValue = this.reg();
514
- this.emit(` ${lengthValue} = call i64 @xt_array_length(i64 ${iterable})`);
515
- const condLabel = this.label("forof.cond");
516
- const bodyLabel = this.label("forof.body");
517
- const updateLabel = this.label("forof.update");
518
- const endLabel = this.label("forof.end");
519
- this.terminate(`br label %${condLabel}`);
520
- this.startBlock(condLabel);
521
- const index = this.reg();
522
- this.emit(` ${index} = load i64, i64* ${indexPtr}`);
523
- const inRange = this.reg();
524
- this.emit(` ${inRange} = call i64 @xt_lt(i64 ${index}, i64 ${lengthValue})`);
525
- const truthy = this.reg();
526
- this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${inRange})`);
527
- const nonzero = this.reg();
528
- this.emit(` ${nonzero} = icmp ne i32 ${truthy}, 0`);
529
- this.terminate(`br i1 ${nonzero}, label %${bodyLabel}, label %${endLabel}`);
530
- this.startBlock(bodyLabel);
531
- const element = this.reg();
532
- this.emit(` ${element} = call i64 @xt_get(i64 ${iterable}, i64 ${index})`);
533
- this.bindLoopVariable(statement.initializer, element);
534
- this.current.loops.push({ breakLabel: endLabel, continueLabel: updateLabel, tryDepth: this.current.tryFrames.length });
535
- this.emitStatement(statement.statement);
536
- this.current.loops.pop();
537
- if (!this.current.terminated)
538
- this.terminate(`br label %${updateLabel}`);
539
- this.startBlock(updateLabel);
540
- const next = this.reg();
541
- this.emit(` ${next} = call i64 @xt_add(i64 ${index}, i64 ${numberLiteral(1)})`);
542
- this.emit(` store i64 ${next}, i64* ${indexPtr}`);
543
- this.terminate(`br label %${condLabel}`);
544
- this.startBlock(endLabel);
545
- }
546
- bindLoopVariable(initializer, value) {
547
- if (initializer.kind === SyntaxKind.VariableDeclarationList) {
548
- const declaration = initializer.declarations[0];
549
- if (!declaration)
550
- return;
551
- const symbol = this.binding.symbolOfDeclaration.get(declaration);
552
- if (symbol) {
553
- if (this.current.slots.has(symbol.id))
554
- this.writeSlot(symbol, value);
555
- else
556
- this.declareSlot(symbol, value);
557
- }
558
- return;
559
- }
560
- this.emitAssignmentTarget(initializer, value);
561
- }
562
- /**
563
- * JavaScript `switch`: test each `case` with strict equality, then run the
564
- * matched clause and fall through into the following clauses until `break`.
565
- */
566
- emitSwitch(statement) {
567
- const discriminant = this.emitExpression(statement.expression);
568
- const endLabel = this.label("switch.end");
569
- const clauses = statement.clauses;
570
- const labels = clauses.map(() => this.label("switch.case"));
571
- const testLabels = clauses.map((clause) => clause.kind === SyntaxKind.CaseClause ? this.label("switch.test") : undefined);
572
- const defaultIndex = clauses.findIndex((clause) => clause.kind === SyntaxKind.DefaultClause);
573
- const defaultLabel = defaultIndex >= 0 ? labels[defaultIndex] : endLabel;
574
- const caseIndexes = clauses
575
- .map((clause, index) => ({ clause, index }))
576
- .filter((entry) => entry.clause.kind === SyntaxKind.CaseClause);
577
- this.terminate(`br label %${caseIndexes.length > 0 ? testLabels[caseIndexes[0].index] : defaultLabel}`);
578
- for (let test = 0; test < caseIndexes.length; test++) {
579
- const entry = caseIndexes[test];
580
- this.startBlock(testLabels[entry.index]);
581
- const caseValue = this.emitExpression(entry.clause.expression);
582
- const equals = this.reg();
583
- this.emit(` ${equals} = call i64 @xt_seq(i64 ${discriminant}, i64 ${caseValue})`);
584
- const truthy = this.reg();
585
- this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${equals})`);
586
- const condition = this.reg();
587
- this.emit(` ${condition} = icmp ne i32 ${truthy}, 0`);
588
- const next = test + 1 < caseIndexes.length ? testLabels[caseIndexes[test + 1].index] : defaultLabel;
589
- this.terminate(`br i1 ${condition}, label %${labels[entry.index]}, label %${next}`);
590
- }
591
- for (let index = 0; index < clauses.length; index++) {
592
- this.startBlock(labels[index]);
593
- const enclosing = this.current.loops[this.current.loops.length - 1];
594
- this.current.loops.push({ breakLabel: endLabel, continueLabel: enclosing?.continueLabel ?? endLabel, tryDepth: this.current.tryFrames.length });
595
- for (const child of clauses[index].statements) {
596
- if (this.current.terminated)
597
- break;
598
- this.emitStatement(child);
599
- }
600
- this.current.loops.pop();
601
- if (!this.current.terminated) {
602
- const next = index + 1 < clauses.length ? labels[index + 1] : endLabel;
603
- this.terminate(`br label %${next}`);
604
- }
605
- }
606
- this.startBlock(endLabel);
607
- }
608
- /**
609
- * `try`/`catch`/`finally` via a runtime setjmp frame. The runtime keeps a
610
- * stack of frames; `xt_throw` longjmps into the innermost one. A `return`
611
- * that exits the protected region skips `finally` (a known limitation).
612
- */
613
- emitTry(statement) {
614
- this.current.usesTry = true;
615
- const frameSlot = this.alloca();
616
- const exceptionSlot = this.alloca();
617
- const flagSlot = this.alloca();
618
- const tryLabel = this.label("try.body");
619
- const catchLabel = this.label("try.catch");
620
- const exceptionLabel = this.label("try.exception");
621
- const rethrowLabel = this.label("try.rethrow");
622
- const finallyLabel = statement.finallyBlock ? this.label("try.finally") : undefined;
623
- const endLabel = this.label("try.end");
624
- const hasCatch = !!statement.catchClause;
625
- const frame = this.reg();
626
- this.emit(` ${frame} = call i8* @xt_try_enter()`);
627
- this.emit(` store i8* ${frame}, i8** ${frameSlot}`);
628
- this.emit(` store i64 0, i64* ${flagSlot}`);
629
- const jump = this.reg();
630
- this.emit(` ${jump} = call i32 @_setjmp(i8* ${frame})`);
631
- const isThrow = this.reg();
632
- this.emit(` ${isThrow} = icmp ne i32 ${jump}, 0`);
633
- const exceptionTarget = hasCatch ? catchLabel : exceptionLabel;
634
- this.terminate(`br i1 ${isThrow}, label %${exceptionTarget}, label %${tryLabel}`);
635
- // Normal completion of the try block.
636
- this.startBlock(tryLabel);
637
- this.current.tryFrames.push(frameSlot);
638
- this.emitStatements(statement.tryBlock.statements);
639
- this.current.tryFrames.pop();
640
- if (!this.current.terminated) {
641
- const currentFrame = this.reg();
642
- this.emit(` ${currentFrame} = load i8*, i8** ${frameSlot}`);
643
- this.emit(` call void @xt_try_leave(i8* ${currentFrame})`);
644
- this.terminate(`br label %${finallyLabel ?? endLabel}`);
645
- }
646
- if (hasCatch) {
647
- this.startBlock(catchLabel);
648
- const currentFrame = this.reg();
649
- this.emit(` ${currentFrame} = load i8*, i8** ${frameSlot}`);
650
- const exception = this.reg();
651
- this.emit(` ${exception} = call i64 @xt_try_exception(i8* ${currentFrame})`);
652
- this.emit(` call void @xt_try_leave(i8* ${currentFrame})`);
653
- this.bindCatchVariable(statement.catchClause.variable, exception);
654
- this.emitStatements(statement.catchClause.block.statements);
655
- if (!this.current.terminated)
656
- this.terminate(`br label %${finallyLabel ?? endLabel}`);
657
- }
658
- else {
659
- // Without a catch clause the exception is remembered, then rethrown
660
- // after `finally` runs.
661
- this.startBlock(exceptionLabel);
662
- const currentFrame = this.reg();
663
- this.emit(` ${currentFrame} = load i8*, i8** ${frameSlot}`);
664
- const exception = this.reg();
665
- this.emit(` ${exception} = call i64 @xt_try_exception(i8* ${currentFrame})`);
666
- this.emit(` call void @xt_try_leave(i8* ${currentFrame})`);
667
- this.emit(` store i64 ${exception}, i64* ${exceptionSlot}`);
668
- this.emit(` store i64 1, i64* ${flagSlot}`);
669
- this.terminate(`br label %${finallyLabel ?? rethrowLabel}`);
670
- }
671
- if (finallyLabel) {
672
- this.startBlock(finallyLabel);
673
- this.emitStatements(statement.finallyBlock.statements);
674
- if (!this.current.terminated) {
675
- if (hasCatch) {
676
- this.terminate(`br label %${endLabel}`);
677
- }
678
- else {
679
- const flag = this.reg();
680
- this.emit(` ${flag} = load i64, i64* ${flagSlot}`);
681
- const truthy = this.reg();
682
- this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${flag})`);
683
- const shouldRethrow = this.reg();
684
- this.emit(` ${shouldRethrow} = icmp ne i32 ${truthy}, 0`);
685
- this.terminate(`br i1 ${shouldRethrow}, label %${rethrowLabel}, label %${endLabel}`);
686
- }
687
- }
688
- }
689
- if (!hasCatch) {
690
- this.startBlock(rethrowLabel);
691
- const exception = this.reg();
692
- this.emit(` ${exception} = load i64, i64* ${exceptionSlot}`);
693
- this.emit(` call void @xt_throw(i64 ${exception})`);
694
- this.terminate("unreachable");
695
- }
696
- this.startBlock(endLabel);
697
- }
698
- bindCatchVariable(variable, value) {
699
- if (!variable)
700
- return;
701
- const symbol = this.binding.symbolOfDeclaration.get(variable);
702
- if (!symbol)
703
- return;
704
- if (this.current.slots.has(symbol.id))
705
- this.writeSlot(symbol, value);
706
- else
707
- this.declareSlot(symbol, value);
708
- }
709
- emitReturn(statement) {
710
- const value = statement.expression ? this.emitExpression(statement.expression) : i64(XT_UNDEFINED);
711
- this.popTryFramesTo(0);
712
- this.terminate(`ret i64 ${value}`);
713
- }
714
- /** Emit `xt_try_leave` for every active frame above `depth` (innermost first). */
715
- popTryFramesTo(depth) {
716
- for (let index = this.current.tryFrames.length - 1; index >= depth; index--) {
717
- const slot = this.current.tryFrames[index];
718
- const frame = this.reg();
719
- this.emit(` ${frame} = load i8*, i8** ${slot}`);
720
- this.emit(` call void @xt_try_leave(i8* ${frame})`);
721
- }
722
- }
723
- // -- expressions ---------------------------------------------------------
724
- emitExpression(node) {
725
- switch (node.kind) {
726
- case SyntaxKind.Identifier:
727
- return this.emitIdentifier(node);
728
- case SyntaxKind.NumericLiteral: {
729
- const value = node.value;
730
- return numberLiteral(value);
731
- }
732
- case SyntaxKind.BigIntLiteral:
733
- return numberLiteral(Number(node.value));
734
- case SyntaxKind.StringLiteral:
735
- return this.stringValue(node.value);
736
- case SyntaxKind.NoSubstitutionTemplateLiteral:
737
- return this.stringValue(node.value);
738
- case SyntaxKind.TemplateLiteral:
739
- return this.emitTemplate(node);
740
- case SyntaxKind.TrueKeyword:
741
- return i64(XT_TRUE);
742
- case SyntaxKind.FalseKeyword:
743
- return i64(XT_FALSE);
744
- case SyntaxKind.NullKeyword:
745
- return i64(XT_NULL);
746
- case SyntaxKind.UndefinedKeyword:
747
- return i64(XT_UNDEFINED);
748
- case SyntaxKind.ParenthesizedExpression:
749
- return this.emitExpression(node.expression);
750
- case SyntaxKind.AsExpression:
751
- case SyntaxKind.SatisfiesExpression:
752
- case SyntaxKind.NonNullExpression:
753
- return this.emitExpression(node.expression);
754
- case SyntaxKind.DeleteExpression:
755
- return this.emitDelete(node);
756
- case SyntaxKind.BinaryExpression:
757
- return this.emitBinaryOrAssignment(node);
758
- case SyntaxKind.PrefixUnaryExpression:
759
- return this.emitPrefix(node);
760
- case SyntaxKind.PostfixUnaryExpression:
761
- return this.emitPostfix(node);
762
- case SyntaxKind.ConditionalExpression:
763
- return this.emitConditional(node);
764
- case SyntaxKind.CallExpression:
765
- return this.emitCall(node);
766
- case SyntaxKind.PropertyAccessExpression:
767
- return this.emitPropertyAccess(node);
768
- case SyntaxKind.ElementAccessExpression:
769
- return this.emitElementAccess(node);
770
- case SyntaxKind.ArrayLiteralExpression:
771
- return this.emitArrayLiteral(node);
772
- case SyntaxKind.ObjectLiteralExpression:
773
- return this.emitObjectLiteral(node);
774
- case SyntaxKind.ArrowFunction:
775
- case SyntaxKind.FunctionExpression:
776
- return this.emitClosure(node);
777
- default:
778
- this.unsupported(node, "expression");
779
- return i64(XT_UNDEFINED);
780
- }
781
- }
782
- emitIdentifier(identifier) {
783
- const symbol = this.binding.symbolOfIdentifier.get(identifier);
784
- if (symbol) {
785
- if (symbol.kind === SymbolKind.Function && !this.current.slots.has(symbol.id)) {
786
- return this.emitFunctionValue(symbol);
787
- }
788
- return this.readSlot(symbol);
789
- }
790
- switch (identifier.text) {
791
- case "undefined":
792
- return i64(XT_UNDEFINED);
793
- case "NaN":
794
- return numberLiteral(NaN);
795
- case "Infinity":
796
- return numberLiteral(Infinity);
797
- case "console":
798
- return i64(XT_UNDEFINED);
799
- case "arguments": {
800
- const argc = this.reg();
801
- this.emit(` ${argc} = load i32, i32* %saved.argc`);
802
- const argv = this.reg();
803
- this.emit(` ${argv} = load i64*, i64** %saved.argv`);
804
- const rest = this.reg();
805
- this.emit(` ${rest} = call i64 @xt_rest_args(i32 ${argc}, i64* ${argv}, i32 0)`);
806
- return rest;
807
- }
808
- default:
809
- this.diagnostics.error(DiagnosticCode.CannotFindName, `Cannot find name '${identifier.text}'`, identifier, this.sourceFile.fileName);
810
- return i64(XT_UNDEFINED);
811
- }
812
- }
813
- emitFunctionValue(symbol) {
814
- const declaration = symbol.declarations[0];
815
- const fn = declaration ? this.binding.functionOfNode.get(declaration) : undefined;
816
- if (!fn)
817
- return i64(XT_UNDEFINED);
818
- const cast = this.reg();
819
- this.emit(` ${cast} = bitcast i64 (i64, i32, i64*)* @${this.functionName(fn)} to i8*`);
820
- const closure = this.reg();
821
- this.emit(` ${closure} = call i64 @xt_closure_new(i8* ${cast}, i32 0, i64* null)`);
822
- return closure;
823
- }
824
- emitTemplate(node) {
825
- let accumulator = this.stringValue(node.head);
826
- for (const span of node.spans) {
827
- const expression = this.emitExpression(span.expression);
828
- const text = this.reg();
829
- this.emit(` ${text} = call i64 @xt_to_string(i64 ${expression})`);
830
- const joined = this.reg();
831
- this.emit(` ${joined} = call i64 @xt_add(i64 ${accumulator}, i64 ${text})`);
832
- accumulator = joined;
833
- if (span.literal.length > 0) {
834
- const literal = this.stringValue(span.literal);
835
- const withLiteral = this.reg();
836
- this.emit(` ${withLiteral} = call i64 @xt_add(i64 ${accumulator}, i64 ${literal})`);
837
- accumulator = withLiteral;
838
- }
839
- }
840
- return accumulator;
841
- }
842
- emitBinaryOrAssignment(node) {
843
- const operator = node.operator;
844
- if (isAssignmentOperator(operator)) {
845
- return this.emitAssignment(node);
846
- }
847
- switch (operator) {
848
- case BinaryOperator.AmpersandAmpersand:
849
- return this.emitShortCircuit(node, "and");
850
- case BinaryOperator.BarBar:
851
- return this.emitShortCircuit(node, "or");
852
- case BinaryOperator.QuestionQuestion:
853
- return this.emitShortCircuit(node, "nullish");
854
- case BinaryOperator.Comma: {
855
- this.emitExpression(node.left);
856
- return this.emitExpression(node.right);
857
- }
858
- default:
859
- break;
860
- }
861
- const left = this.emitExpression(node.left);
862
- const right = this.emitExpression(node.right);
863
- const runtime = BINARY_RUNTIME[operator];
864
- if (!runtime) {
865
- this.unsupported(node, `operator '${operator}'`);
866
- return i64(XT_UNDEFINED);
867
- }
868
- const result = this.reg();
869
- this.emit(` ${result} = call i64 @${runtime}(i64 ${left}, i64 ${right})`);
870
- return result;
871
- }
872
- emitShortCircuit(node, kind) {
873
- const result = this.alloca();
874
- const left = this.emitExpression(node.left);
875
- this.emit(` store i64 ${left}, i64* ${result}`);
876
- const rightLabel = this.label("sc.right");
877
- const endLabel = this.label("sc.end");
878
- let condition;
879
- if (kind === "and") {
880
- const truthy = this.reg();
881
- this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${left})`);
882
- condition = this.reg();
883
- this.emit(` ${condition} = icmp ne i32 ${truthy}, 0`);
884
- }
885
- else if (kind === "or") {
886
- const truthy = this.reg();
887
- this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${left})`);
888
- const isTruthy = this.reg();
889
- this.emit(` ${isTruthy} = icmp ne i32 ${truthy}, 0`);
890
- condition = this.reg();
891
- this.emit(` ${condition} = xor i1 ${isTruthy}, true`);
892
- }
893
- else {
894
- const nullish = this.reg();
895
- this.emit(` ${nullish} = call i64 @xt_is_nullish(i64 ${left})`);
896
- const truthy = this.reg();
897
- this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${nullish})`);
898
- condition = this.reg();
899
- this.emit(` ${condition} = icmp ne i32 ${truthy}, 0`);
900
- }
901
- this.terminate(`br i1 ${condition}, label %${rightLabel}, label %${endLabel}`);
902
- this.startBlock(rightLabel);
903
- const right = this.emitExpression(node.right);
904
- this.emit(` store i64 ${right}, i64* ${result}`);
905
- this.terminate(`br label %${endLabel}`);
906
- this.startBlock(endLabel);
907
- const value = this.reg();
908
- this.emit(` ${value} = load i64, i64* ${result}`);
909
- return value;
910
- }
911
- emitConditional(node) {
912
- const result = this.alloca();
913
- const condition = this.emitExpression(node.condition);
914
- const truthy = this.reg();
915
- this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${condition})`);
916
- const nonzero = this.reg();
917
- this.emit(` ${nonzero} = icmp ne i32 ${truthy}, 0`);
918
- const trueLabel = this.label("cond.true");
919
- const falseLabel = this.label("cond.false");
920
- const endLabel = this.label("cond.end");
921
- this.terminate(`br i1 ${nonzero}, label %${trueLabel}, label %${falseLabel}`);
922
- this.startBlock(trueLabel);
923
- const whenTrue = this.emitExpression(node.whenTrue);
924
- this.emit(` store i64 ${whenTrue}, i64* ${result}`);
925
- this.terminate(`br label %${endLabel}`);
926
- this.startBlock(falseLabel);
927
- const whenFalse = this.emitExpression(node.whenFalse);
928
- this.emit(` store i64 ${whenFalse}, i64* ${result}`);
929
- this.terminate(`br label %${endLabel}`);
930
- this.startBlock(endLabel);
931
- const value = this.reg();
932
- this.emit(` ${value} = load i64, i64* ${result}`);
933
- return value;
934
- }
935
- emitPrefix(node) {
936
- switch (node.operator) {
937
- case PrefixUnaryOperator.Minus: {
938
- const operand = this.emitExpression(node.operand);
939
- const result = this.reg();
940
- this.emit(` ${result} = call i64 @xt_neg(i64 ${operand})`);
941
- return result;
942
- }
943
- case PrefixUnaryOperator.Plus: {
944
- const operand = this.emitExpression(node.operand);
945
- const result = this.reg();
946
- this.emit(` ${result} = call i64 @xt_pos(i64 ${operand})`);
947
- return result;
948
- }
949
- case PrefixUnaryOperator.Exclamation: {
950
- const operand = this.emitExpression(node.operand);
951
- const result = this.reg();
952
- this.emit(` ${result} = call i64 @xt_not(i64 ${operand})`);
953
- return result;
954
- }
955
- case PrefixUnaryOperator.Tilde: {
956
- const operand = this.emitExpression(node.operand);
957
- const result = this.reg();
958
- this.emit(` ${result} = call i64 @xt_bit_not(i64 ${operand})`);
959
- return result;
960
- }
961
- case PrefixUnaryOperator.TypeOf: {
962
- const operand = this.emitExpression(node.operand);
963
- const result = this.reg();
964
- this.emit(` ${result} = call i64 @xt_typeof(i64 ${operand})`);
965
- return result;
966
- }
967
- case PrefixUnaryOperator.Void:
968
- if (node.operand)
969
- this.emitExpression(node.operand);
970
- return i64(XT_UNDEFINED);
971
- case PrefixUnaryOperator.PlusPlus:
972
- case PrefixUnaryOperator.MinusMinus: {
973
- const one = numberLiteral(1);
974
- const current = this.emitExpression(node.operand);
975
- const fn = node.operator === PrefixUnaryOperator.PlusPlus ? "xt_add" : "xt_sub";
976
- const next = this.reg();
977
- this.emit(` ${next} = call i64 @${fn}(i64 ${current}, i64 ${one})`);
978
- this.emitAssignmentTarget(node.operand, next);
979
- return next;
980
- }
981
- default:
982
- this.unsupported(node, `unary operator '${node.operator}'`);
983
- return i64(XT_UNDEFINED);
984
- }
985
- }
986
- emitPostfix(node) {
987
- const one = numberLiteral(1);
988
- const current = this.emitExpression(node.operand);
989
- const fn = node.operator === PostfixUnaryOperator.PlusPlus ? "xt_add" : "xt_sub";
990
- const next = this.reg();
991
- this.emit(` ${next} = call i64 @${fn}(i64 ${current}, i64 ${one})`);
992
- this.emitAssignmentTarget(node.operand, next);
993
- return current;
994
- }
995
- emitAssignment(node) {
996
- const operator = node.operator;
997
- const target = node.left;
998
- let value;
999
- if (operator === AssignmentOperator.Assign) {
1000
- value = this.emitExpression(node.right);
1001
- }
1002
- else if (operator === AssignmentOperator.AmpersandAmpersandAssign ||
1003
- operator === AssignmentOperator.BarBarAssign ||
1004
- operator === AssignmentOperator.QuestionQuestionAssign) {
1005
- value = this.emitLogicalAssignment(node);
1006
- }
1007
- else {
1008
- const current = this.emitExpression(target);
1009
- const right = this.emitExpression(node.right);
1010
- const binaryOperator = compoundToBinary(operator);
1011
- const runtime = BINARY_RUNTIME[binaryOperator];
1012
- if (!runtime) {
1013
- this.unsupported(node, `assignment operator '${operator}'`);
1014
- return i64(XT_UNDEFINED);
1015
- }
1016
- const result = this.reg();
1017
- this.emit(` ${result} = call i64 @${runtime}(i64 ${current}, i64 ${right})`);
1018
- value = result;
1019
- }
1020
- this.emitAssignmentTarget(target, value);
1021
- return value;
1022
- }
1023
- emitLogicalAssignment(node) {
1024
- const operator = node.operator;
1025
- const target = node.left;
1026
- const current = this.emitExpression(target);
1027
- const result = this.alloca();
1028
- this.emit(` store i64 ${current}, i64* ${result}`);
1029
- const rightLabel = this.label("assign.right");
1030
- const endLabel = this.label("assign.end");
1031
- let condition;
1032
- if (operator === AssignmentOperator.AmpersandAmpersandAssign) {
1033
- const truthy = this.reg();
1034
- this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${current})`);
1035
- condition = this.reg();
1036
- this.emit(` ${condition} = icmp ne i32 ${truthy}, 0`);
1037
- }
1038
- else if (operator === AssignmentOperator.BarBarAssign) {
1039
- const truthy = this.reg();
1040
- this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${current})`);
1041
- const isTruthy = this.reg();
1042
- this.emit(` ${isTruthy} = icmp ne i32 ${truthy}, 0`);
1043
- condition = this.reg();
1044
- this.emit(` ${condition} = xor i1 ${isTruthy}, true`);
1045
- }
1046
- else {
1047
- const nullish = this.reg();
1048
- this.emit(` ${nullish} = call i64 @xt_is_nullish(i64 ${current})`);
1049
- const truthy = this.reg();
1050
- this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${nullish})`);
1051
- condition = this.reg();
1052
- this.emit(` ${condition} = icmp ne i32 ${truthy}, 0`);
1053
- }
1054
- this.terminate(`br i1 ${condition}, label %${rightLabel}, label %${endLabel}`);
1055
- this.startBlock(rightLabel);
1056
- const right = this.emitExpression(node.right);
1057
- this.emit(` store i64 ${right}, i64* ${result}`);
1058
- this.terminate(`br label %${endLabel}`);
1059
- this.startBlock(endLabel);
1060
- const value = this.reg();
1061
- this.emit(` ${value} = load i64, i64* ${result}`);
1062
- this.emitAssignmentTarget(target, value);
1063
- return value;
1064
- }
1065
- emitAssignmentTarget(target, value) {
1066
- switch (target.kind) {
1067
- case SyntaxKind.Identifier: {
1068
- const symbol = this.binding.symbolOfIdentifier.get(target);
1069
- if (symbol)
1070
- this.writeSlot(symbol, value);
1071
- return;
1072
- }
1073
- case SyntaxKind.PropertyAccessExpression: {
1074
- const access = target;
1075
- const object = this.emitExpression(access.expression);
1076
- const key = this.stringValue(access.name.text);
1077
- this.emit(` call i64 @xt_set(i64 ${object}, i64 ${key}, i64 ${value})`);
1078
- return;
1079
- }
1080
- case SyntaxKind.ElementAccessExpression: {
1081
- const access = target;
1082
- const object = this.emitExpression(access.expression);
1083
- const key = this.emitExpression(access.argumentExpression);
1084
- this.emit(` call i64 @xt_set(i64 ${object}, i64 ${key}, i64 ${value})`);
1085
- return;
1086
- }
1087
- case SyntaxKind.ParenthesizedExpression:
1088
- this.emitAssignmentTarget(target.expression, value);
1089
- return;
1090
- default:
1091
- this.unsupported(target, "assignment target");
1092
- }
1093
- }
1094
- emitCall(node) {
1095
- const callee = node.expression;
1096
- if (node.optional) {
1097
- const calleeValue = this.emitExpression(callee);
1098
- return this.emitOptional(calleeValue, () => {
1099
- const args = this.emitArguments(node.arguments);
1100
- const result = this.reg();
1101
- this.emit(` ${result} = call i64 @xt_closure_call(i64 ${calleeValue}, i32 ${args.argc}, i64* ${args.ptr})`);
1102
- return result;
1103
- });
1104
- }
1105
- if ((callee.kind === SyntaxKind.PropertyAccessExpression || callee.kind === SyntaxKind.ElementAccessExpression) &&
1106
- callee.optional) {
1107
- // `obj?.method(args)` / `obj?.[key](args)`: guard the receiver, then
1108
- // dispatch through the runtime, which understands object methods as well
1109
- // as the built-in array/string methods.
1110
- const access = callee;
1111
- const object = this.emitExpression(access.expression);
1112
- return this.emitOptional(object, () => {
1113
- const name = access.kind === SyntaxKind.PropertyAccessExpression
1114
- ? this.stringValue(access.name.text)
1115
- : this.emitExpression(access.argumentExpression);
1116
- const args = this.emitArguments(node.arguments);
1117
- const result = this.reg();
1118
- this.emit(` ${result} = call i64 @xt_call_method(i64 ${object}, i64 ${name}, i32 ${args.argc}, i64* ${args.ptr})`);
1119
- return result;
1120
- });
1121
- }
1122
- if (callee.kind === SyntaxKind.PropertyAccessExpression) {
1123
- const special = this.tryEmitBuiltinCall(node, callee);
1124
- if (special)
1125
- return special;
1126
- }
1127
- if (callee.kind === SyntaxKind.Identifier) {
1128
- const symbol = this.binding.symbolOfIdentifier.get(callee);
1129
- if (symbol && symbol.kind === SymbolKind.Function) {
1130
- const declaration = symbol.declarations[0];
1131
- const fn = declaration ? this.binding.functionOfNode.get(declaration) : undefined;
1132
- if (fn) {
1133
- const args = this.emitArguments(node.arguments);
1134
- const result = this.reg();
1135
- this.emit(` ${result} = call i64 @${this.functionName(fn)}(i64 ${i64(XT_UNDEFINED)}, i32 ${args.argc}, i64* ${args.ptr})`);
1136
- return result;
1137
- }
1138
- }
1139
- const globalFunction = !symbol ? GLOBAL_FUNCTIONS[callee.text] : undefined;
1140
- if (globalFunction) {
1141
- const args = this.emitArguments(node.arguments);
1142
- const result = this.reg();
1143
- this.emit(` ${result} = call i64 @${globalFunction}(i32 ${args.argc}, i64* ${args.ptr})`);
1144
- return result;
1145
- }
1146
- const builtin = this.builtins[callee.text];
1147
- if (!symbol && builtin) {
1148
- this.extraDeclarations.add(builtin.returnVoid ? `declare void @${builtin.symbol}(i32, i64*)` : `declare i64 @${builtin.symbol}(i32, i64*)`);
1149
- const args = this.emitArguments(node.arguments);
1150
- if (builtin.returnVoid) {
1151
- this.emit(` call void @${builtin.symbol}(i32 ${args.argc}, i64* ${args.ptr})`);
1152
- return i64(XT_UNDEFINED);
1153
- }
1154
- const result = this.reg();
1155
- this.emit(` ${result} = call i64 @${builtin.symbol}(i32 ${args.argc}, i64* ${args.ptr})`);
1156
- return result;
1157
- }
1158
- }
1159
- const calleeValue = this.emitExpression(callee);
1160
- const args = this.emitArguments(node.arguments);
1161
- const result = this.reg();
1162
- this.emit(` ${result} = call i64 @xt_closure_call(i64 ${calleeValue}, i32 ${args.argc}, i64* ${args.ptr})`);
1163
- return result;
1164
- }
1165
- tryEmitBuiltinCall(node, callee) {
1166
- const target = callee.expression;
1167
- const method = callee.name.text;
1168
- const targetIdentifier = target.kind === SyntaxKind.Identifier ? target : undefined;
1169
- const targetSymbol = targetIdentifier ? this.binding.symbolOfIdentifier.get(targetIdentifier) : undefined;
1170
- if (targetIdentifier && targetIdentifier.text === "console" && !targetSymbol) {
1171
- const consoleFn = CONSOLE_METHODS[method];
1172
- if (consoleFn) {
1173
- const args = this.emitArguments(node.arguments);
1174
- this.emit(` call void @${consoleFn}(i32 ${args.argc}, i64* ${args.ptr})`);
1175
- return i64(XT_UNDEFINED);
1176
- }
1177
- return undefined;
1178
- }
1179
- if (targetIdentifier && targetIdentifier.text === "Math" && !targetSymbol) {
1180
- if (!MATH_FUNCTIONS.has(method))
1181
- return undefined;
1182
- const name = this.stringValue(method);
1183
- const args = this.emitArguments(node.arguments);
1184
- const result = this.reg();
1185
- this.emit(` ${result} = call i64 @xt_math_call(i64 ${name}, i32 ${args.argc}, i64* ${args.ptr})`);
1186
- return result;
1187
- }
1188
- if (targetIdentifier && targetIdentifier.text === "Object" && !targetSymbol) {
1189
- if (method === "keys" || method === "values" || method === "entries") {
1190
- const argument = node.arguments.length > 0 ? this.emitExpression(node.arguments[0]) : i64(XT_UNDEFINED);
1191
- const fn = method === "keys" ? "xt_object_keys" : method === "values" ? "xt_object_values" : "xt_object_entries";
1192
- return this.runtimeCall(fn, [argument]);
1193
- }
1194
- if (method === "assign") {
1195
- const args = this.emitArguments(node.arguments);
1196
- const result = this.reg();
1197
- this.emit(` ${result} = call i64 @xt_object_assign(i32 ${args.argc}, i64* ${args.ptr})`);
1198
- return result;
1199
- }
1200
- return undefined;
1201
- }
1202
- if (BUILTIN_METHODS.has(method)) {
1203
- const object = this.emitExpression(target);
1204
- const name = this.stringValue(method);
1205
- const args = this.emitArguments(node.arguments);
1206
- const result = this.reg();
1207
- this.emit(` ${result} = call i64 @xt_call_method(i64 ${object}, i64 ${name}, i32 ${args.argc}, i64* ${args.ptr})`);
1208
- return result;
1209
- }
1210
- return undefined;
1211
- }
1212
- runtimeCall(name, args) {
1213
- const result = this.reg();
1214
- this.emit(` ${result} = call i64 @${name}(${args.map((a) => `i64 ${a}`).join(", ")})`);
1215
- return result;
1216
- }
1217
- emitArguments(args) {
1218
- if (args.length === 0)
1219
- return { argc: 0, ptr: "null" };
1220
- const ptr = `%args${this.current.allocas.length}`;
1221
- this.current.allocas.push(`${ptr} = alloca i64, i32 ${args.length}`);
1222
- for (let index = 0; index < args.length; index++) {
1223
- const value = this.emitExpression(args[index]);
1224
- const slot = this.reg();
1225
- this.emit(` ${slot} = getelementptr i64, i64* ${ptr}, i32 ${index}`);
1226
- this.emit(` store i64 ${value}, i64* ${slot}`);
1227
- }
1228
- return { argc: args.length, ptr };
1229
- }
1230
- emitDelete(node) {
1231
- const target = node.expression;
1232
- if (target.kind === SyntaxKind.PropertyAccessExpression) {
1233
- const access = target;
1234
- const object = this.emitExpression(access.expression);
1235
- const key = this.stringValue(access.name.text);
1236
- return this.runtimeCall("xt_delete", [object, key]);
1237
- }
1238
- if (target.kind === SyntaxKind.ElementAccessExpression) {
1239
- const access = target;
1240
- const object = this.emitExpression(access.expression);
1241
- const key = this.emitExpression(access.argumentExpression);
1242
- return this.runtimeCall("xt_delete", [object, key]);
1243
- }
1244
- return i64(XT_TRUE);
1245
- }
1246
- emitPropertyAccess(node) {
1247
- if (node.name.text in MATH_CONSTANTS &&
1248
- node.expression.kind === SyntaxKind.Identifier &&
1249
- node.expression.text === "Math" &&
1250
- !this.binding.symbolOfIdentifier.get(node.expression)) {
1251
- return numberLiteral(MATH_CONSTANTS[node.name.text]);
1252
- }
1253
- const object = this.emitExpression(node.expression);
1254
- const access = () => {
1255
- if (node.name.text === "length") {
1256
- const result = this.reg();
1257
- this.emit(` ${result} = call i64 @xt_array_length(i64 ${object})`);
1258
- return result;
1259
- }
1260
- const key = this.stringValue(node.name.text);
1261
- const result = this.reg();
1262
- this.emit(` ${result} = call i64 @xt_get(i64 ${object}, i64 ${key})`);
1263
- return result;
1264
- };
1265
- return node.optional ? this.emitOptional(object, access) : access();
1266
- }
1267
- emitElementAccess(node) {
1268
- const object = this.emitExpression(node.expression);
1269
- const access = () => {
1270
- const key = this.emitExpression(node.argumentExpression);
1271
- const result = this.reg();
1272
- this.emit(` ${result} = call i64 @xt_get(i64 ${object}, i64 ${key})`);
1273
- return result;
1274
- };
1275
- return node.optional ? this.emitOptional(object, access) : access();
1276
- }
1277
- /**
1278
- * Evaluate `object` once and, when it is neither `null` nor `undefined`, run
1279
- * `compute` to produce the value; otherwise short-circuit to `undefined`.
1280
- */
1281
- emitOptional(objectValue, compute) {
1282
- const result = this.alloca();
1283
- this.emit(` store i64 ${i64(XT_UNDEFINED)}, i64* ${result}`);
1284
- const nullish = this.reg();
1285
- this.emit(` ${nullish} = call i64 @xt_is_nullish(i64 ${objectValue})`);
1286
- const truthy = this.reg();
1287
- this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${nullish})`);
1288
- const condition = this.reg();
1289
- this.emit(` ${condition} = icmp ne i32 ${truthy}, 0`);
1290
- const someLabel = this.label("opt.some");
1291
- const endLabel = this.label("opt.end");
1292
- this.terminate(`br i1 ${condition}, label %${endLabel}, label %${someLabel}`);
1293
- this.startBlock(someLabel);
1294
- const value = compute();
1295
- this.emit(` store i64 ${value}, i64* ${result}`);
1296
- this.terminate(`br label %${endLabel}`);
1297
- this.startBlock(endLabel);
1298
- const merged = this.reg();
1299
- this.emit(` ${merged} = load i64, i64* ${result}`);
1300
- return merged;
1301
- }
1302
- emitArrayLiteral(node) {
1303
- const hasSpread = node.elements.some((element) => element.kind === SyntaxKind.SpreadElement);
1304
- if (!hasSpread) {
1305
- const args = this.emitArguments(node.elements);
1306
- const result = this.reg();
1307
- this.emit(` ${result} = call i64 @xt_array_new(i32 ${args.argc}, i64* ${args.ptr})`);
1308
- return result;
1309
- }
1310
- let array = this.runtimeCall("xt_array_new", [`0`, `null`]);
1311
- for (const element of node.elements) {
1312
- if (element.kind === SyntaxKind.SpreadElement) {
1313
- const spread = this.emitExpression(element.expression);
1314
- array = this.runtimeCall("xt_array_spread", [array, spread]);
1315
- }
1316
- else {
1317
- const value = this.emitExpression(element);
1318
- array = this.runtimeCall("xt_array_push", [array, value]);
1319
- }
1320
- }
1321
- return array;
1322
- }
1323
- emitObjectLiteral(node) {
1324
- const object = this.reg();
1325
- this.emit(` ${object} = call i64 @xt_object_new()`);
1326
- for (const property of node.properties) {
1327
- if (property.kind === SyntaxKind.PropertyAssignment) {
1328
- const name = propertyNameText(property.name);
1329
- const key = this.stringValue(name);
1330
- const value = this.emitExpression(property.initializer);
1331
- this.emit(` call i64 @xt_set(i64 ${object}, i64 ${key}, i64 ${value})`);
1332
- }
1333
- else if (property.kind === SyntaxKind.ShorthandPropertyAssignment) {
1334
- const identifier = property.name;
1335
- const key = this.stringValue(identifier.text);
1336
- const value = this.emitIdentifier(identifier);
1337
- this.emit(` call i64 @xt_set(i64 ${object}, i64 ${key}, i64 ${value})`);
1338
- }
1339
- else if (property.kind === SyntaxKind.SpreadElement) {
1340
- const spread = this.emitExpression(property.expression);
1341
- this.emit(` call i64 @xt_object_spread(i64 ${object}, i64 ${spread})`);
1342
- }
1343
- else {
1344
- this.unsupported(property, "object spread");
1345
- }
1346
- }
1347
- return object;
1348
- }
1349
- emitClosure(node) {
1350
- const fn = this.binding.functionOfNode.get(node);
1351
- if (!fn) {
1352
- this.unsupported(node, "closure");
1353
- return i64(XT_UNDEFINED);
1354
- }
1355
- let envPtr = "null";
1356
- if (fn.captures.length > 0) {
1357
- envPtr = `%env${this.current.allocas.length}`;
1358
- this.current.allocas.push(`${envPtr} = alloca i64, i32 ${fn.captures.length}`);
1359
- for (let index = 0; index < fn.captures.length; index++) {
1360
- const symbol = fn.captures[index];
1361
- // The current function must be able to read the capture: either from
1362
- // its own slot (local) or from one of its own capture slots.
1363
- const slot = this.current.slots.get(symbol.id);
1364
- let raw;
1365
- if (slot) {
1366
- raw = this.reg();
1367
- this.emit(` ${raw} = load i64, i64* ${slot.ptr}`);
1368
- }
1369
- else {
1370
- const outer = this.binding.functionOfNode.get(node);
1371
- void outer;
1372
- raw = i64(XT_UNDEFINED);
1373
- }
1374
- const target = this.reg();
1375
- this.emit(` ${target} = getelementptr i64, i64* ${envPtr}, i32 ${index}`);
1376
- this.emit(` store i64 ${raw}, i64* ${target}`);
1377
- }
1378
- }
1379
- const cast = this.reg();
1380
- this.emit(` ${cast} = bitcast i64 (i64, i32, i64*)* @${this.functionName(fn)} to i8*`);
1381
- const closure = this.reg();
1382
- this.emit(` ${closure} = call i64 @xt_closure_new(i8* ${cast}, i32 ${fn.captures.length}, i64* ${envPtr})`);
1383
- return closure;
1384
- }
1385
- // -- string pool ---------------------------------------------------------
1386
- stringValue(text) {
1387
- const entry = this.internString(text);
1388
- const result = this.reg();
1389
- this.emit(` ${result} = call i64 @xt_string_new(i8* ${entry.label}, i64 ${entry.length})`);
1390
- return result;
1391
- }
1392
- internString(text) {
1393
- const existing = this.strings.get(text);
1394
- if (existing)
1395
- return existing;
1396
- const label = `@.str.${this.stringCounter++}`;
1397
- const bytes = utf8Bytes(text);
1398
- const entry = { label, length: bytes.length };
1399
- this.strings.set(text, entry);
1400
- this.globals.push(`${label} = private unnamed_addr constant [${bytes.length + 1} x i8] c"${escapeBytes(bytes)}\\00"`);
1401
- return entry;
1402
- }
1403
- unsupported(node, what) {
1404
- const label = kindName(node.kind);
1405
- this.diagnostics.error(DiagnosticCode.UnsupportedFeature, `xbintsc does not yet support this ${what} (${label})`, node, this.sourceFile.fileName);
1406
- }
1407
- }
1408
- const BINARY_RUNTIME = {
1409
- [BinaryOperator.Add]: "xt_add",
1410
- [BinaryOperator.Subtract]: "xt_sub",
1411
- [BinaryOperator.Multiply]: "xt_mul",
1412
- [BinaryOperator.Divide]: "xt_div",
1413
- [BinaryOperator.Remainder]: "xt_mod",
1414
- [BinaryOperator.Exponent]: "xt_pow",
1415
- [BinaryOperator.LessThan]: "xt_lt",
1416
- [BinaryOperator.LessThanEquals]: "xt_le",
1417
- [BinaryOperator.GreaterThan]: "xt_gt",
1418
- [BinaryOperator.GreaterThanEquals]: "xt_ge",
1419
- [BinaryOperator.EqualsEquals]: "xt_eq",
1420
- [BinaryOperator.ExclamationEquals]: "xt_ne",
1421
- [BinaryOperator.EqualsEqualsEquals]: "xt_seq",
1422
- [BinaryOperator.ExclamationEqualsEquals]: "xt_sne",
1423
- [BinaryOperator.Ampersand]: "xt_bit_and",
1424
- [BinaryOperator.Bar]: "xt_bit_or",
1425
- [BinaryOperator.Caret]: "xt_bit_xor",
1426
- [BinaryOperator.LessThanLessThan]: "xt_shl",
1427
- [BinaryOperator.GreaterThanGreaterThan]: "xt_shr",
1428
- [BinaryOperator.GreaterThanGreaterThanGreaterThan]: "xt_ushr",
1429
- [BinaryOperator.In]: "xt_in",
1430
- };
1431
- const CONSOLE_METHODS = {
1432
- log: "xt_console_log",
1433
- info: "xt_console_info",
1434
- warn: "xt_console_warn",
1435
- error: "xt_console_error",
1436
- };
1437
- const MATH_FUNCTIONS = new Set([
1438
- "abs", "floor", "ceil", "round", "trunc", "sqrt", "cbrt", "pow", "exp", "log", "log2", "log10",
1439
- "sin", "cos", "tan", "asin", "acos", "atan", "atan2", "hypot", "sign", "random", "min", "max",
1440
- ]);
1441
- const MATH_CONSTANTS = {
1442
- PI: Math.PI,
1443
- E: Math.E,
1444
- LN2: Math.LN2,
1445
- LN10: Math.LN10,
1446
- LOG2E: Math.LOG2E,
1447
- LOG10E: Math.LOG10E,
1448
- SQRT2: Math.SQRT2,
1449
- SQRT1_2: Math.SQRT1_2,
1450
- };
1451
- const GLOBAL_FUNCTIONS = {
1452
- parseInt: "xt_parse_int",
1453
- parseFloat: "xt_parse_float",
1454
- isNaN: "xt_is_nan",
1455
- isFinite: "xt_is_finite",
1456
- Number: "xt_number_ctor",
1457
- String: "xt_string_ctor",
1458
- Boolean: "xt_boolean_ctor",
1459
- };
1460
- const BUILTIN_METHODS = new Set([
1461
- "push", "pop", "shift", "unshift", "join", "slice", "indexOf", "includes", "map", "forEach", "filter",
1462
- "reduce", "concat", "reverse", "charAt", "charCodeAt", "substring", "substr", "split", "toUpperCase",
1463
- "toLowerCase", "trim", "replace", "repeat", "startsWith", "endsWith",
1464
- ]);
1465
- const ASSIGNMENT_OPERATORS = new Set([
1466
- "=",
1467
- "+=",
1468
- "-=",
1469
- "*=",
1470
- "/=",
1471
- "%=",
1472
- "**=",
1473
- "<<=",
1474
- ">>=",
1475
- ">>>=",
1476
- "&=",
1477
- "|=",
1478
- "^=",
1479
- "&&=",
1480
- "||=",
1481
- "??=",
1482
- ]);
1483
- function isAssignmentOperator(operator) {
1484
- return ASSIGNMENT_OPERATORS.has(operator);
1485
- }
1486
- function compoundToBinary(operator) {
1487
- const text = operator.slice(0, -1);
1488
- return text;
1489
- }
1490
- function propertyNameText(name) {
1491
- switch (name.kind) {
1492
- case SyntaxKind.Identifier:
1493
- case SyntaxKind.PrivateIdentifier:
1494
- return name.text;
1495
- case SyntaxKind.StringLiteral:
1496
- return name.value;
1497
- case SyntaxKind.NumericLiteral:
1498
- return String(name.value);
1499
- default:
1500
- return "";
1501
- }
1502
- }
1503
- /** Human readable syntax kind for diagnostics (const enums have no reverse map). */
1504
- function kindName(kind) {
1505
- const entry = KIND_NAMES.get(kind);
1506
- return entry ?? String(kind);
1507
- }
1508
- const KIND_NAMES = new Map([
1509
- [SyntaxKind.CallExpression, "call expression"],
1510
- [SyntaxKind.NewExpression, "new expression"],
1511
- [SyntaxKind.ClassDeclaration, "class declaration"],
1512
- [SyntaxKind.ClassExpression, "class expression"],
1513
- [SyntaxKind.EnumDeclaration, "enum declaration"],
1514
- [SyntaxKind.SwitchStatement, "switch statement"],
1515
- [SyntaxKind.TryStatement, "try statement"],
1516
- [SyntaxKind.SpreadElement, "spread element"],
1517
- [SyntaxKind.ThisKeyword, "this expression"],
1518
- [SyntaxKind.RegularExpressionLiteral, "regular expression"],
1519
- [SyntaxKind.TaggedTemplateExpression, "tagged template"],
1520
- [SyntaxKind.DeleteExpression, "delete expression"],
1521
- [SyntaxKind.AwaitExpression, "await expression"],
1522
- [SyntaxKind.YieldExpression, "yield expression"],
1523
- [SyntaxKind.SwitchStatement, "switch statement"],
1524
- ]);
1525
- function utf8Bytes(text) {
1526
- const bytes = [];
1527
- for (const character of text) {
1528
- const code = character.codePointAt(0);
1529
- if (code < 0x80)
1530
- bytes.push(code);
1531
- else if (code < 0x800) {
1532
- bytes.push(0xc0 | (code >> 6), 0x80 | (code & 0x3f));
1533
- }
1534
- else if (code < 0x10000) {
1535
- bytes.push(0xe0 | (code >> 12), 0x80 | ((code >> 6) & 0x3f), 0x80 | (code & 0x3f));
1536
- }
1537
- else {
1538
- bytes.push(0xf0 | (code >> 18), 0x80 | ((code >> 12) & 0x3f), 0x80 | ((code >> 6) & 0x3f), 0x80 | (code & 0x3f));
1539
- }
1540
- }
1541
- return bytes;
1542
- }
1543
- function escapeBytes(bytes) {
1544
- return bytes
1545
- .map((byte) => {
1546
- if (byte === 0x22)
1547
- return '\\22';
1548
- if (byte === 0x5c)
1549
- return '\\5C';
1550
- if (byte >= 0x20 && byte < 0x7f)
1551
- return String.fromCharCode(byte);
1552
- return `\\${byte.toString(16).toUpperCase().padStart(2, "0")}`;
1553
- })
1554
- .join("");
1555
- }
22
+ export { generate } from "./generator/generator.js";
1556
23
  //# sourceMappingURL=llvm.js.map