xbintsc 0.1.6 → 0.1.9

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 (152) 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/binder/binder.d.ts +35 -1
  30. package/dist/src/binder/binder.js +138 -0
  31. package/dist/src/binder/binder.js.map +1 -1
  32. package/dist/src/codegen/generator/calls.d.ts +26 -0
  33. package/dist/src/codegen/generator/calls.js +373 -0
  34. package/dist/src/codegen/generator/calls.js.map +1 -0
  35. package/dist/src/codegen/generator/context.d.ts +47 -0
  36. package/dist/src/codegen/generator/context.js +164 -0
  37. package/dist/src/codegen/generator/context.js.map +1 -0
  38. package/dist/src/codegen/generator/expressions.d.ts +25 -0
  39. package/dist/src/codegen/generator/expressions.js +454 -0
  40. package/dist/src/codegen/generator/expressions.js.map +1 -0
  41. package/dist/src/codegen/generator/generator.d.ts +22 -0
  42. package/dist/src/codegen/generator/generator.js +21 -0
  43. package/dist/src/codegen/generator/generator.js.map +1 -0
  44. package/dist/src/codegen/generator/module.d.ts +19 -0
  45. package/dist/src/codegen/generator/module.js +214 -0
  46. package/dist/src/codegen/generator/module.js.map +1 -0
  47. package/dist/src/codegen/generator/state.d.ts +42 -0
  48. package/dist/src/codegen/generator/state.js +125 -0
  49. package/dist/src/codegen/generator/state.js.map +1 -0
  50. package/dist/src/codegen/generator/statements.d.ts +22 -0
  51. package/dist/src/codegen/generator/statements.js +433 -0
  52. package/dist/src/codegen/generator/statements.js.map +1 -0
  53. package/dist/src/codegen/generator/tables.d.ts +23 -0
  54. package/dist/src/codegen/generator/tables.js +186 -0
  55. package/dist/src/codegen/generator/tables.js.map +1 -0
  56. package/dist/src/codegen/llvm.d.ts +4 -13
  57. package/dist/src/codegen/llvm.js +4 -1537
  58. package/dist/src/codegen/llvm.js.map +1 -1
  59. package/dist/src/driver/compiler.js +36 -11
  60. package/dist/src/driver/compiler.js.map +1 -1
  61. package/dist/src/driver/modules.d.ts +24 -0
  62. package/dist/src/driver/modules.js +347 -0
  63. package/dist/src/driver/modules.js.map +1 -0
  64. package/dist/src/lexer/char.d.ts +78 -0
  65. package/dist/src/lexer/char.js +106 -0
  66. package/dist/src/lexer/char.js.map +1 -0
  67. package/dist/src/lexer/scanner.js +1 -98
  68. package/dist/src/lexer/scanner.js.map +1 -1
  69. package/dist/src/parser/context.d.ts +58 -0
  70. package/dist/src/parser/context.js +174 -0
  71. package/dist/src/parser/context.js.map +1 -0
  72. package/dist/src/parser/declarations.d.ts +26 -0
  73. package/dist/src/parser/declarations.js +461 -0
  74. package/dist/src/parser/declarations.js.map +1 -0
  75. package/dist/src/parser/expressions.d.ts +27 -0
  76. package/dist/src/parser/expressions.js +440 -0
  77. package/dist/src/parser/expressions.js.map +1 -0
  78. package/dist/src/parser/helpers.d.ts +7 -0
  79. package/dist/src/parser/helpers.js +36 -0
  80. package/dist/src/parser/helpers.js.map +1 -0
  81. package/dist/src/parser/literals.d.ts +16 -0
  82. package/dist/src/parser/literals.js +193 -0
  83. package/dist/src/parser/literals.js.map +1 -0
  84. package/dist/src/parser/modules.d.ts +12 -0
  85. package/dist/src/parser/modules.js +175 -0
  86. package/dist/src/parser/modules.js.map +1 -0
  87. package/dist/src/parser/operators.d.ts +12 -0
  88. package/dist/src/parser/operators.js +172 -0
  89. package/dist/src/parser/operators.js.map +1 -0
  90. package/dist/src/parser/parser.d.ts +17 -122
  91. package/dist/src/parser/parser.js +18 -2311
  92. package/dist/src/parser/parser.js.map +1 -1
  93. package/dist/src/parser/speculation.d.ts +6 -0
  94. package/dist/src/parser/speculation.js +10 -0
  95. package/dist/src/parser/speculation.js.map +1 -0
  96. package/dist/src/parser/statements.d.ts +28 -0
  97. package/dist/src/parser/statements.js +402 -0
  98. package/dist/src/parser/statements.js.map +1 -0
  99. package/dist/src/parser/types.d.ts +21 -0
  100. package/dist/src/parser/types.js +338 -0
  101. package/dist/src/parser/types.js.map +1 -0
  102. package/dist/tests/codegen/llvm.test.js +1 -1
  103. package/dist/tests/codegen/llvm.test.js.map +1 -1
  104. package/dist/tests/e2e/compile.test.js +86 -0
  105. package/dist/tests/e2e/compile.test.js.map +1 -1
  106. package/package.json +1 -1
  107. package/runtime/rt.h +55 -4
  108. package/runtime/rt_internal.h +169 -0
  109. package/runtime/xt_alloc.c +38 -0
  110. package/runtime/xt_builtins.c +238 -0
  111. package/runtime/xt_containers.c +312 -0
  112. package/runtime/xt_io.c +246 -0
  113. package/runtime/xt_promise.c +292 -0
  114. package/runtime/xt_stdlib.c +556 -0
  115. package/runtime/xt_stdlib2.c +1043 -0
  116. package/runtime/xt_values.c +291 -0
  117. package/scripts/build-runtime.mjs +8 -1
  118. package/src/ast/common.ts +210 -0
  119. package/src/ast/declarations.ts +130 -0
  120. package/src/ast/expressions.ts +275 -0
  121. package/src/ast/kinds.ts +176 -0
  122. package/src/ast/modules.ts +73 -0
  123. package/src/ast/nodes.ts +11 -1101
  124. package/src/ast/operators.ts +76 -0
  125. package/src/ast/statements.ts +148 -0
  126. package/src/ast/types.ts +173 -0
  127. package/src/binder/binder.ts +196 -0
  128. package/src/codegen/generator/calls.ts +433 -0
  129. package/src/codegen/generator/context.ts +190 -0
  130. package/src/codegen/generator/expressions.ts +508 -0
  131. package/src/codegen/generator/generator.ts +35 -0
  132. package/src/codegen/generator/module.ts +244 -0
  133. package/src/codegen/generator/state.ts +169 -0
  134. package/src/codegen/generator/statements.ts +480 -0
  135. package/src/codegen/generator/tables.ts +206 -0
  136. package/src/codegen/llvm.ts +4 -1720
  137. package/src/driver/compiler.ts +45 -12
  138. package/src/driver/modules.ts +377 -0
  139. package/src/lexer/char.ts +116 -0
  140. package/src/lexer/scanner.ts +9 -109
  141. package/src/parser/context.ts +196 -0
  142. package/src/parser/declarations.ts +502 -0
  143. package/src/parser/expressions.ts +468 -0
  144. package/src/parser/helpers.ts +42 -0
  145. package/src/parser/literals.ts +207 -0
  146. package/src/parser/modules.ts +194 -0
  147. package/src/parser/operators.ts +181 -0
  148. package/src/parser/parser.ts +35 -2373
  149. package/src/parser/speculation.ts +9 -0
  150. package/src/parser/statements.ts +457 -0
  151. package/src/parser/types.ts +365 -0
  152. package/runtime/xt_runtime.c +0 -1372
@@ -0,0 +1,508 @@
1
+ /**
2
+ * Expression lowering: operators, assignments, conditionals, templates and
3
+ * identifier resolution.
4
+ */
5
+
6
+ import {
7
+ SyntaxKind,
8
+ type ArrayLiteralExpression,
9
+ type ArrowFunction,
10
+ type AwaitExpression,
11
+ type BinaryExpression,
12
+ type CallExpression,
13
+ type ClassExpression,
14
+ type ConditionalExpression,
15
+ type DeleteExpression,
16
+ type ElementAccessExpression,
17
+ type Expression,
18
+ type FunctionExpression,
19
+ type Identifier,
20
+ type NewExpression,
21
+ type ObjectLiteralExpression,
22
+ type PostfixUnaryExpression,
23
+ type PrefixUnaryExpression,
24
+ type PropertyAccessExpression,
25
+ type TemplateLiteral,
26
+ } from "../../ast/nodes.js";
27
+ import {
28
+ AssignmentOperator,
29
+ BinaryOperator,
30
+ PrefixUnaryOperator,
31
+ PostfixUnaryOperator,
32
+ } from "../../ast/nodes.js";
33
+ import { SymbolKind, type SymbolInfo } from "../../binder/binder.js";
34
+ import { DiagnosticCode } from "../../diagnostics/diagnostic.js";
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";
37
+ import type { Generator } from "./generator.js";
38
+
39
+ export interface ExpressionMethods {
40
+ emitExpression(this: Generator, node: Expression): string;
41
+ emitIdentifier(this: Generator, identifier: Identifier): string;
42
+ emitFunctionValue(this: Generator, symbol: SymbolInfo): string;
43
+ emitTemplate(this: Generator, node: TemplateLiteral): string;
44
+ emitBinaryOrAssignment(this: Generator, node: BinaryExpression): string;
45
+ emitShortCircuit(this: Generator, node: BinaryExpression, kind: "and" | "or" | "nullish"): string;
46
+ emitConditional(this: Generator, node: ConditionalExpression): string;
47
+ emitPrefix(this: Generator, node: PrefixUnaryExpression): string;
48
+ emitPostfix(this: Generator, node: PostfixUnaryExpression): string;
49
+ emitAssignment(this: Generator, node: BinaryExpression): string;
50
+ emitLogicalAssignment(this: Generator, node: BinaryExpression): string;
51
+ emitAssignmentTarget(this: Generator, target: Expression, value: string): void;
52
+ emitThis(this: Generator): string;
53
+ emitNew(this: Generator, node: NewExpression): string;
54
+ emitAwait(this: Generator, node: AwaitExpression): string;
55
+ }
56
+
57
+ export const expressionMethods: ExpressionMethods = {
58
+ emitExpression(node: Expression): string {
59
+ switch (node.kind) {
60
+ case SyntaxKind.Identifier:
61
+ return this.emitIdentifier(node as Identifier);
62
+ case SyntaxKind.NumericLiteral: {
63
+ const value = (node as { value: number }).value;
64
+ return numberLiteral(value);
65
+ }
66
+ case SyntaxKind.BigIntLiteral:
67
+ return numberLiteral(Number((node as { value: bigint }).value));
68
+ case SyntaxKind.StringLiteral:
69
+ return this.stringValue((node as { value: string }).value);
70
+ case SyntaxKind.NoSubstitutionTemplateLiteral:
71
+ return this.stringValue((node as { value: string }).value);
72
+ case SyntaxKind.TemplateLiteral:
73
+ return this.emitTemplate(node as TemplateLiteral);
74
+ case SyntaxKind.TrueKeyword:
75
+ return i64(XT_TRUE);
76
+ case SyntaxKind.FalseKeyword:
77
+ return i64(XT_FALSE);
78
+ case SyntaxKind.NullKeyword:
79
+ return i64(XT_NULL);
80
+ case SyntaxKind.UndefinedKeyword:
81
+ return i64(XT_UNDEFINED);
82
+ case SyntaxKind.ParenthesizedExpression:
83
+ return this.emitExpression((node as { expression: Expression }).expression);
84
+ case SyntaxKind.AsExpression:
85
+ case SyntaxKind.SatisfiesExpression:
86
+ case SyntaxKind.NonNullExpression:
87
+ return this.emitExpression((node as { expression: Expression }).expression);
88
+ case SyntaxKind.DeleteExpression:
89
+ return this.emitDelete(node as DeleteExpression);
90
+ case SyntaxKind.ThisKeyword:
91
+ return this.emitThis();
92
+ case SyntaxKind.NewExpression:
93
+ return this.emitNew(node as NewExpression);
94
+ case SyntaxKind.AwaitExpression:
95
+ return this.emitAwait(node as AwaitExpression);
96
+ case SyntaxKind.ClassExpression: {
97
+ const info = this.binding.classOfNode.get(node);
98
+ if (!info) {
99
+ this.unsupported(node, "class expression");
100
+ return i64(XT_UNDEFINED);
101
+ }
102
+ this.emitClassSetup(info);
103
+ return this.emitClassReference(info);
104
+ }
105
+ case SyntaxKind.BinaryExpression:
106
+ return this.emitBinaryOrAssignment(node as BinaryExpression);
107
+ case SyntaxKind.PrefixUnaryExpression:
108
+ return this.emitPrefix(node as PrefixUnaryExpression);
109
+ case SyntaxKind.PostfixUnaryExpression:
110
+ return this.emitPostfix(node as PostfixUnaryExpression);
111
+ case SyntaxKind.ConditionalExpression:
112
+ return this.emitConditional(node as ConditionalExpression);
113
+ case SyntaxKind.CallExpression:
114
+ return this.emitCall(node as CallExpression);
115
+ case SyntaxKind.PropertyAccessExpression:
116
+ return this.emitPropertyAccess(node as PropertyAccessExpression);
117
+ case SyntaxKind.ElementAccessExpression:
118
+ return this.emitElementAccess(node as ElementAccessExpression);
119
+ case SyntaxKind.ArrayLiteralExpression:
120
+ return this.emitArrayLiteral(node as ArrayLiteralExpression);
121
+ case SyntaxKind.ObjectLiteralExpression:
122
+ return this.emitObjectLiteral(node as ObjectLiteralExpression);
123
+ case SyntaxKind.ArrowFunction:
124
+ case SyntaxKind.FunctionExpression:
125
+ return this.emitClosure(node as ArrowFunction | FunctionExpression);
126
+ default:
127
+ this.unsupported(node, "expression");
128
+ return i64(XT_UNDEFINED);
129
+ }
130
+ },
131
+
132
+ emitIdentifier(identifier: Identifier): string {
133
+ const symbol = this.binding.symbolOfIdentifier.get(identifier);
134
+ if (symbol) {
135
+ if (symbol.kind === SymbolKind.Class) {
136
+ const declaration = symbol.declarations[0];
137
+ const info = declaration ? this.binding.classOfNode.get(declaration) : undefined;
138
+ if (info) return this.emitClassReference(info);
139
+ }
140
+ if (symbol.kind === SymbolKind.Function && !this.current.slots.has(symbol.id)) {
141
+ return this.emitFunctionValue(symbol);
142
+ }
143
+ return this.readSlot(symbol);
144
+ }
145
+ switch (identifier.text) {
146
+ case "undefined":
147
+ return i64(XT_UNDEFINED);
148
+ case "NaN":
149
+ return numberLiteral(NaN);
150
+ case "Infinity":
151
+ return numberLiteral(Infinity);
152
+ case "console":
153
+ return i64(XT_UNDEFINED);
154
+ case "arguments": {
155
+ const argc = this.reg();
156
+ this.emit(` ${argc} = load i32, i32* %saved.argc`);
157
+ const argv = this.reg();
158
+ this.emit(` ${argv} = load i64*, i64** %saved.argv`);
159
+ const rest = this.reg();
160
+ this.emit(` ${rest} = call i64 @xt_rest_args(i32 ${argc}, i64* ${argv}, i32 0)`);
161
+ return rest;
162
+ }
163
+ default:
164
+ this.diagnostics.error(
165
+ DiagnosticCode.CannotFindName,
166
+ `Cannot find name '${identifier.text}'`,
167
+ identifier,
168
+ this.sourceFile.fileName,
169
+ );
170
+ return i64(XT_UNDEFINED);
171
+ }
172
+ },
173
+
174
+ emitFunctionValue(symbol: SymbolInfo): string {
175
+ const declaration = symbol.declarations[0];
176
+ const fn = declaration ? this.binding.functionOfNode.get(declaration) : undefined;
177
+ if (!fn) return i64(XT_UNDEFINED);
178
+ const cast = this.reg();
179
+ this.emit(` ${cast} = bitcast i64 (i64, i64, i32, i64*)* @${this.functionName(fn)} to i8*`);
180
+ const closure = this.reg();
181
+ this.emit(` ${closure} = call i64 @xt_closure_new(i8* ${cast}, i32 0, i64* null)`);
182
+ return closure;
183
+ },
184
+
185
+ emitThis(): string {
186
+ if (!this.current.thisPtr) return i64(XT_UNDEFINED);
187
+ const value = this.reg();
188
+ this.emit(` ${value} = load i64, i64* ${this.current.thisPtr}`);
189
+ return value;
190
+ },
191
+
192
+ emitAwait(node: AwaitExpression): string {
193
+ const value = this.emitExpression(node.expression);
194
+ const result = this.reg();
195
+ this.emit(` ${result} = call i64 @xt_await(i64 ${value})`);
196
+ return result;
197
+ },
198
+
199
+ emitNew(node: NewExpression): string {
200
+ const callee = node.expression;
201
+ if (callee.kind === SyntaxKind.Identifier) {
202
+ const identifier = callee as Identifier;
203
+ const symbol = this.binding.symbolOfIdentifier.get(identifier);
204
+ if (symbol && symbol.kind === SymbolKind.Class) {
205
+ const declaration = symbol.declarations[0];
206
+ const info = declaration ? this.binding.classOfNode.get(declaration) : undefined;
207
+ if (info) {
208
+ const ctor = this.emitClassReference(info);
209
+ const args = this.emitArguments(node.arguments);
210
+ const result = this.reg();
211
+ this.emit(` ${result} = call i64 @xt_new(i64 ${ctor}, i32 ${args.argc}, i64* ${args.ptr})`);
212
+ return result;
213
+ }
214
+ }
215
+ if (!symbol) {
216
+ const ctor = CTOR_FUNCTIONS[identifier.text];
217
+ if (ctor) {
218
+ const args = this.emitArguments(node.arguments);
219
+ const result = this.reg();
220
+ this.emit(` ${result} = call i64 @${ctor}(i32 ${args.argc}, i64* ${args.ptr})`);
221
+ return result;
222
+ }
223
+ if (identifier.text === "Object") {
224
+ if (node.arguments.length > 0) return this.emitExpression(node.arguments[0]!);
225
+ return this.runtimeCall("xt_object_new", []);
226
+ }
227
+ }
228
+ }
229
+ const ctorValue = this.emitExpression(callee);
230
+ const args = this.emitArguments(node.arguments);
231
+ const result = this.reg();
232
+ this.emit(` ${result} = call i64 @xt_new(i64 ${ctorValue}, i32 ${args.argc}, i64* ${args.ptr})`);
233
+ return result;
234
+ },
235
+
236
+ emitTemplate(node: TemplateLiteral): string {
237
+ let accumulator = this.stringValue(node.head);
238
+ for (const span of node.spans) {
239
+ const expression = this.emitExpression(span.expression);
240
+ const text = this.reg();
241
+ this.emit(` ${text} = call i64 @xt_to_string(i64 ${expression})`);
242
+ const joined = this.reg();
243
+ this.emit(` ${joined} = call i64 @xt_add(i64 ${accumulator}, i64 ${text})`);
244
+ accumulator = joined;
245
+ if (span.literal.length > 0) {
246
+ const literal = this.stringValue(span.literal);
247
+ const withLiteral = this.reg();
248
+ this.emit(` ${withLiteral} = call i64 @xt_add(i64 ${accumulator}, i64 ${literal})`);
249
+ accumulator = withLiteral;
250
+ }
251
+ }
252
+ return accumulator;
253
+ },
254
+
255
+ emitBinaryOrAssignment(node: BinaryExpression): string {
256
+ const operator = node.operator as string;
257
+ if (isAssignmentOperator(operator)) {
258
+ return this.emitAssignment(node);
259
+ }
260
+ switch (operator) {
261
+ case BinaryOperator.AmpersandAmpersand:
262
+ return this.emitShortCircuit(node, "and");
263
+ case BinaryOperator.BarBar:
264
+ return this.emitShortCircuit(node, "or");
265
+ case BinaryOperator.QuestionQuestion:
266
+ return this.emitShortCircuit(node, "nullish");
267
+ case BinaryOperator.Comma: {
268
+ this.emitExpression(node.left);
269
+ return this.emitExpression(node.right);
270
+ }
271
+ default:
272
+ break;
273
+ }
274
+ const left = this.emitExpression(node.left);
275
+ const right = this.emitExpression(node.right);
276
+ const runtime = BINARY_RUNTIME[operator];
277
+ if (!runtime) {
278
+ this.unsupported(node, `operator '${operator}'`);
279
+ return i64(XT_UNDEFINED);
280
+ }
281
+ const result = this.reg();
282
+ this.emit(` ${result} = call i64 @${runtime}(i64 ${left}, i64 ${right})`);
283
+ return result;
284
+ },
285
+
286
+ emitShortCircuit(node: BinaryExpression, kind: "and" | "or" | "nullish"): string {
287
+ const result = this.alloca();
288
+ const left = this.emitExpression(node.left);
289
+ this.emit(` store i64 ${left}, i64* ${result}`);
290
+ const rightLabel = this.label("sc.right");
291
+ const endLabel = this.label("sc.end");
292
+ let condition: string;
293
+ if (kind === "and") {
294
+ const truthy = this.reg();
295
+ this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${left})`);
296
+ condition = this.reg();
297
+ this.emit(` ${condition} = icmp ne i32 ${truthy}, 0`);
298
+ } else if (kind === "or") {
299
+ const truthy = this.reg();
300
+ this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${left})`);
301
+ const isTruthy = this.reg();
302
+ this.emit(` ${isTruthy} = icmp ne i32 ${truthy}, 0`);
303
+ condition = this.reg();
304
+ this.emit(` ${condition} = xor i1 ${isTruthy}, true`);
305
+ } else {
306
+ const nullish = this.reg();
307
+ this.emit(` ${nullish} = call i64 @xt_is_nullish(i64 ${left})`);
308
+ const truthy = this.reg();
309
+ this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${nullish})`);
310
+ condition = this.reg();
311
+ this.emit(` ${condition} = icmp ne i32 ${truthy}, 0`);
312
+ }
313
+ this.terminate(`br i1 ${condition}, label %${rightLabel}, label %${endLabel}`);
314
+ this.startBlock(rightLabel);
315
+ const right = this.emitExpression(node.right);
316
+ this.emit(` store i64 ${right}, i64* ${result}`);
317
+ this.terminate(`br label %${endLabel}`);
318
+ this.startBlock(endLabel);
319
+ const value = this.reg();
320
+ this.emit(` ${value} = load i64, i64* ${result}`);
321
+ return value;
322
+ },
323
+
324
+ emitConditional(node: ConditionalExpression): string {
325
+ const result = this.alloca();
326
+ const condition = this.emitExpression(node.condition);
327
+ const truthy = this.reg();
328
+ this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${condition})`);
329
+ const nonzero = this.reg();
330
+ this.emit(` ${nonzero} = icmp ne i32 ${truthy}, 0`);
331
+ const trueLabel = this.label("cond.true");
332
+ const falseLabel = this.label("cond.false");
333
+ const endLabel = this.label("cond.end");
334
+ this.terminate(`br i1 ${nonzero}, label %${trueLabel}, label %${falseLabel}`);
335
+ this.startBlock(trueLabel);
336
+ const whenTrue = this.emitExpression(node.whenTrue);
337
+ this.emit(` store i64 ${whenTrue}, i64* ${result}`);
338
+ this.terminate(`br label %${endLabel}`);
339
+ this.startBlock(falseLabel);
340
+ const whenFalse = this.emitExpression(node.whenFalse);
341
+ this.emit(` store i64 ${whenFalse}, i64* ${result}`);
342
+ this.terminate(`br label %${endLabel}`);
343
+ this.startBlock(endLabel);
344
+ const value = this.reg();
345
+ this.emit(` ${value} = load i64, i64* ${result}`);
346
+ return value;
347
+ },
348
+
349
+ emitPrefix(node: PrefixUnaryExpression): string {
350
+ switch (node.operator) {
351
+ case PrefixUnaryOperator.Minus: {
352
+ const operand = this.emitExpression(node.operand);
353
+ const result = this.reg();
354
+ this.emit(` ${result} = call i64 @xt_neg(i64 ${operand})`);
355
+ return result;
356
+ }
357
+ case PrefixUnaryOperator.Plus: {
358
+ const operand = this.emitExpression(node.operand);
359
+ const result = this.reg();
360
+ this.emit(` ${result} = call i64 @xt_pos(i64 ${operand})`);
361
+ return result;
362
+ }
363
+ case PrefixUnaryOperator.Exclamation: {
364
+ const operand = this.emitExpression(node.operand);
365
+ const result = this.reg();
366
+ this.emit(` ${result} = call i64 @xt_not(i64 ${operand})`);
367
+ return result;
368
+ }
369
+ case PrefixUnaryOperator.Tilde: {
370
+ const operand = this.emitExpression(node.operand);
371
+ const result = this.reg();
372
+ this.emit(` ${result} = call i64 @xt_bit_not(i64 ${operand})`);
373
+ return result;
374
+ }
375
+ case PrefixUnaryOperator.TypeOf: {
376
+ const operand = this.emitExpression(node.operand);
377
+ const result = this.reg();
378
+ this.emit(` ${result} = call i64 @xt_typeof(i64 ${operand})`);
379
+ return result;
380
+ }
381
+ case PrefixUnaryOperator.Void:
382
+ if (node.operand) this.emitExpression(node.operand);
383
+ return i64(XT_UNDEFINED);
384
+ case PrefixUnaryOperator.PlusPlus:
385
+ case PrefixUnaryOperator.MinusMinus: {
386
+ const one = numberLiteral(1);
387
+ const current = this.emitExpression(node.operand);
388
+ const fn = node.operator === PrefixUnaryOperator.PlusPlus ? "xt_add" : "xt_sub";
389
+ const next = this.reg();
390
+ this.emit(` ${next} = call i64 @${fn}(i64 ${current}, i64 ${one})`);
391
+ this.emitAssignmentTarget(node.operand, next);
392
+ return next;
393
+ }
394
+ default:
395
+ this.unsupported(node, `unary operator '${node.operator}'`);
396
+ return i64(XT_UNDEFINED);
397
+ }
398
+ },
399
+
400
+ emitPostfix(node: PostfixUnaryExpression): string {
401
+ const one = numberLiteral(1);
402
+ const current = this.emitExpression(node.operand);
403
+ const fn = node.operator === PostfixUnaryOperator.PlusPlus ? "xt_add" : "xt_sub";
404
+ const next = this.reg();
405
+ this.emit(` ${next} = call i64 @${fn}(i64 ${current}, i64 ${one})`);
406
+ this.emitAssignmentTarget(node.operand, next);
407
+ return current;
408
+ },
409
+
410
+ emitAssignment(node: BinaryExpression): string {
411
+ const operator = node.operator as unknown as AssignmentOperator;
412
+ const target = node.left;
413
+ let value: string;
414
+ if (operator === AssignmentOperator.Assign) {
415
+ value = this.emitExpression(node.right);
416
+ } else if (
417
+ operator === AssignmentOperator.AmpersandAmpersandAssign ||
418
+ operator === AssignmentOperator.BarBarAssign ||
419
+ operator === AssignmentOperator.QuestionQuestionAssign
420
+ ) {
421
+ value = this.emitLogicalAssignment(node);
422
+ } else {
423
+ const current = this.emitExpression(target);
424
+ const right = this.emitExpression(node.right);
425
+ const binaryOperator = compoundToBinary(operator);
426
+ const runtime = BINARY_RUNTIME[binaryOperator];
427
+ if (!runtime) {
428
+ this.unsupported(node, `assignment operator '${operator}'`);
429
+ return i64(XT_UNDEFINED);
430
+ }
431
+ const result = this.reg();
432
+ this.emit(` ${result} = call i64 @${runtime}(i64 ${current}, i64 ${right})`);
433
+ value = result;
434
+ }
435
+ this.emitAssignmentTarget(target, value);
436
+ return value;
437
+ },
438
+
439
+ emitLogicalAssignment(node: BinaryExpression): string {
440
+ const operator = node.operator as unknown as AssignmentOperator;
441
+ const target = node.left;
442
+ const current = this.emitExpression(target);
443
+ const result = this.alloca();
444
+ this.emit(` store i64 ${current}, i64* ${result}`);
445
+ const rightLabel = this.label("assign.right");
446
+ const endLabel = this.label("assign.end");
447
+ let condition: string;
448
+ if (operator === AssignmentOperator.AmpersandAmpersandAssign) {
449
+ const truthy = this.reg();
450
+ this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${current})`);
451
+ condition = this.reg();
452
+ this.emit(` ${condition} = icmp ne i32 ${truthy}, 0`);
453
+ } else if (operator === AssignmentOperator.BarBarAssign) {
454
+ const truthy = this.reg();
455
+ this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${current})`);
456
+ const isTruthy = this.reg();
457
+ this.emit(` ${isTruthy} = icmp ne i32 ${truthy}, 0`);
458
+ condition = this.reg();
459
+ this.emit(` ${condition} = xor i1 ${isTruthy}, true`);
460
+ } else {
461
+ const nullish = this.reg();
462
+ this.emit(` ${nullish} = call i64 @xt_is_nullish(i64 ${current})`);
463
+ const truthy = this.reg();
464
+ this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${nullish})`);
465
+ condition = this.reg();
466
+ this.emit(` ${condition} = icmp ne i32 ${truthy}, 0`);
467
+ }
468
+ this.terminate(`br i1 ${condition}, label %${rightLabel}, label %${endLabel}`);
469
+ this.startBlock(rightLabel);
470
+ const right = this.emitExpression(node.right);
471
+ this.emit(` store i64 ${right}, i64* ${result}`);
472
+ this.terminate(`br label %${endLabel}`);
473
+ this.startBlock(endLabel);
474
+ const value = this.reg();
475
+ this.emit(` ${value} = load i64, i64* ${result}`);
476
+ this.emitAssignmentTarget(target, value);
477
+ return value;
478
+ },
479
+
480
+ emitAssignmentTarget(target: Expression, value: string): void {
481
+ switch (target.kind) {
482
+ case SyntaxKind.Identifier: {
483
+ const symbol = this.binding.symbolOfIdentifier.get(target as Identifier);
484
+ if (symbol) this.writeSlot(symbol, value);
485
+ return;
486
+ }
487
+ case SyntaxKind.PropertyAccessExpression: {
488
+ const access = target as PropertyAccessExpression;
489
+ const object = this.emitExpression(access.expression);
490
+ const key = this.stringValue(access.name.text);
491
+ this.emit(` call i64 @xt_set(i64 ${object}, i64 ${key}, i64 ${value})`);
492
+ return;
493
+ }
494
+ case SyntaxKind.ElementAccessExpression: {
495
+ const access = target as ElementAccessExpression;
496
+ const object = this.emitExpression(access.expression);
497
+ const key = this.emitExpression(access.argumentExpression);
498
+ this.emit(` call i64 @xt_set(i64 ${object}, i64 ${key}, i64 ${value})`);
499
+ return;
500
+ }
501
+ case SyntaxKind.ParenthesizedExpression:
502
+ this.emitAssignmentTarget((target as { expression: Expression }).expression, value);
503
+ return;
504
+ default:
505
+ this.unsupported(target, "assignment target");
506
+ }
507
+ },
508
+ };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Assembles the LLVM IR generator from its method groups.
3
+ *
4
+ * The implementation is split across `context` (shared state and primitives)
5
+ * and several method-group modules. Each group contributes an interface plus a
6
+ * plain object of methods; they are merged onto the prototype here so the
7
+ * groups can freely call one another through `this`.
8
+ */
9
+
10
+ import type { SourceFileNode } from "../../ast/nodes.js";
11
+ import type { DiagnosticBag } from "../../diagnostics/diagnostic.js";
12
+ import { GeneratorContext } from "./context.js";
13
+ import { moduleMethods, type ModuleMethods } from "./module.js";
14
+ import { statementMethods, type StatementMethods } from "./statements.js";
15
+ import { expressionMethods, type ExpressionMethods } from "./expressions.js";
16
+ import { callMethods, type CallMethods } from "./calls.js";
17
+ import type { CodegenOptions, CodegenResult } from "./state.js";
18
+
19
+ export class Generator extends GeneratorContext {}
20
+
21
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
22
+ export interface Generator extends ModuleMethods, StatementMethods, ExpressionMethods, CallMethods {}
23
+
24
+ Object.assign(Generator.prototype, moduleMethods, statementMethods, expressionMethods, callMethods);
25
+
26
+ export type { CodegenOptions, CodegenResult } from "./state.js";
27
+
28
+ export function generate(
29
+ sourceFile: SourceFileNode,
30
+ diagnostics: DiagnosticBag,
31
+ options: CodegenOptions = {},
32
+ ): CodegenResult {
33
+ const generator = new Generator(sourceFile, diagnostics, options);
34
+ return { ir: generator.run(), binding: generator.binding };
35
+ }