xbintsc 0.3.3 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/ast/expressions.d.ts +8 -0
- package/dist/src/binder/binder.js +1 -0
- package/dist/src/binder/binder.js.map +1 -1
- package/dist/src/binder/references.d.ts +1 -1
- package/dist/src/binder/references.js +34 -5
- package/dist/src/binder/references.js.map +1 -1
- package/dist/src/binder/types.d.ts +4 -0
- package/dist/src/binder/types.js.map +1 -1
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/codegen/generator/calls/access.d.ts +9 -1
- package/dist/src/codegen/generator/calls/access.js +146 -13
- package/dist/src/codegen/generator/calls/access.js.map +1 -1
- package/dist/src/codegen/generator/calls/closures.d.ts +1 -0
- package/dist/src/codegen/generator/calls/closures.js +25 -0
- package/dist/src/codegen/generator/calls/closures.js.map +1 -1
- package/dist/src/codegen/generator/calls/invocation.js +12 -1
- package/dist/src/codegen/generator/calls/invocation.js.map +1 -1
- package/dist/src/codegen/generator/calls/literals.js +12 -4
- package/dist/src/codegen/generator/calls/literals.js.map +1 -1
- package/dist/src/codegen/generator/calls/super.js +4 -7
- package/dist/src/codegen/generator/calls/super.js.map +1 -1
- package/dist/src/codegen/generator/context.d.ts +21 -2
- package/dist/src/codegen/generator/context.js +42 -5
- package/dist/src/codegen/generator/context.js.map +1 -1
- package/dist/src/codegen/generator/expressions/assignment.js +1 -3
- package/dist/src/codegen/generator/expressions/assignment.js.map +1 -1
- package/dist/src/codegen/generator/expressions/primary.d.ts +3 -1
- package/dist/src/codegen/generator/expressions/primary.js +99 -1
- package/dist/src/codegen/generator/expressions/primary.js.map +1 -1
- package/dist/src/codegen/generator/generator.js.map +1 -1
- package/dist/src/codegen/generator/module.js +25 -15
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/state.d.ts +15 -0
- package/dist/src/codegen/generator/state.js +28 -1
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/statements/control-flow.d.ts +3 -1
- package/dist/src/codegen/generator/statements/control-flow.js +85 -0
- package/dist/src/codegen/generator/statements/control-flow.js.map +1 -1
- package/dist/src/codegen/generator/statements/dispatch.js +15 -6
- package/dist/src/codegen/generator/statements/dispatch.js.map +1 -1
- package/dist/src/codegen/generator/statements/loops.d.ts +3 -0
- package/dist/src/codegen/generator/statements/loops.js +25 -11
- package/dist/src/codegen/generator/statements/loops.js.map +1 -1
- package/dist/src/codegen/generator/tables.d.ts +7 -0
- package/dist/src/codegen/generator/tables.js +41 -1
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/driver/compiler.d.ts +1 -1
- package/dist/src/driver/compiler.js +33 -1
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/modules.d.ts +4 -3
- package/dist/src/driver/modules.js +61 -4
- package/dist/src/driver/modules.js.map +1 -1
- package/dist/src/driver/toolchain.js +1 -1
- package/dist/src/driver/toolchain.js.map +1 -1
- package/dist/src/lexer/scanner.js.map +1 -1
- package/dist/src/parser/declarations/classes.js +4 -0
- package/dist/src/parser/declarations/classes.js.map +1 -1
- package/dist/src/parser/declarations/type-members.js.map +1 -1
- package/dist/src/parser/expressions.d.ts +2 -1
- package/dist/src/parser/expressions.js +10 -1
- package/dist/src/parser/expressions.js.map +1 -1
- package/dist/src/parser/helpers.js +2 -2
- package/dist/src/parser/helpers.js.map +1 -1
- package/dist/src/parser/literals.js +79 -6
- package/dist/src/parser/literals.js.map +1 -1
- package/dist/src/parser/modules.js.map +1 -1
- package/dist/src/parser/parser.js +0 -1
- package/dist/src/parser/parser.js.map +1 -1
- package/dist/src/parser/statements.d.ts +1 -0
- package/dist/src/parser/statements.js +13 -1
- package/dist/src/parser/statements.js.map +1 -1
- package/dist/src/parser/types.js +2 -2
- package/dist/src/parser/types.js.map +1 -1
- package/dist/tests/ast/factory.test.d.ts +1 -0
- package/dist/tests/ast/factory.test.js +35 -0
- package/dist/tests/ast/factory.test.js.map +1 -0
- package/dist/tests/ast/visitor.test.d.ts +1 -0
- package/dist/tests/ast/visitor.test.js +53 -0
- package/dist/tests/ast/visitor.test.js.map +1 -0
- package/dist/tests/cli/main.test.js +109 -9
- package/dist/tests/cli/main.test.js.map +1 -1
- package/dist/tests/codegen/closures.test.d.ts +1 -0
- package/dist/tests/codegen/closures.test.js +32 -0
- package/dist/tests/codegen/closures.test.js.map +1 -0
- package/dist/tests/codegen/expressions.test.d.ts +1 -0
- package/dist/tests/codegen/expressions.test.js +149 -0
- package/dist/tests/codegen/expressions.test.js.map +1 -0
- package/dist/tests/codegen/llvm.test.js +14 -0
- package/dist/tests/codegen/llvm.test.js.map +1 -1
- package/dist/tests/codegen/values.test.d.ts +1 -0
- package/dist/tests/codegen/values.test.js +48 -0
- package/dist/tests/codegen/values.test.js.map +1 -0
- package/dist/tests/diagnostics/diagnostic.test.d.ts +1 -0
- package/dist/tests/diagnostics/diagnostic.test.js +117 -0
- package/dist/tests/diagnostics/diagnostic.test.js.map +1 -0
- package/dist/tests/diagnostics/source.test.d.ts +1 -0
- package/dist/tests/diagnostics/source.test.js +58 -0
- package/dist/tests/diagnostics/source.test.js.map +1 -0
- package/dist/tests/driver/modules.test.d.ts +1 -0
- package/dist/tests/driver/modules.test.js +140 -0
- package/dist/tests/driver/modules.test.js.map +1 -0
- package/dist/tests/driver/paths.test.js +17 -1
- package/dist/tests/driver/paths.test.js.map +1 -1
- package/dist/tests/driver/runtime-lib.test.d.ts +1 -0
- package/dist/tests/driver/runtime-lib.test.js +19 -0
- package/dist/tests/driver/runtime-lib.test.js.map +1 -0
- package/dist/tests/driver/toolchain-provider.test.js +91 -0
- package/dist/tests/driver/toolchain-provider.test.js.map +1 -1
- package/dist/tests/driver/toolchain.test.js +11 -1
- package/dist/tests/driver/toolchain.test.js.map +1 -1
- package/dist/tests/e2e/compile-node.test.js +14 -1
- package/dist/tests/e2e/compile-node.test.js.map +1 -1
- package/dist/tests/e2e/compile.test.js +465 -2
- package/dist/tests/e2e/compile.test.js.map +1 -1
- package/dist/tests/e2e/differential.test.d.ts +7 -0
- package/dist/tests/e2e/differential.test.js +581 -0
- package/dist/tests/e2e/differential.test.js.map +1 -0
- package/dist/tests/e2e/harness.d.ts +21 -0
- package/dist/tests/e2e/harness.js +26 -0
- package/dist/tests/e2e/harness.js.map +1 -1
- package/dist/tests/extensions/node.test.d.ts +1 -0
- package/dist/tests/extensions/node.test.js +32 -0
- package/dist/tests/extensions/node.test.js.map +1 -0
- package/dist/tests/lexer/identifiers.test.d.ts +1 -0
- package/dist/tests/lexer/identifiers.test.js +13 -0
- package/dist/tests/lexer/identifiers.test.js.map +1 -0
- package/dist/tests/lexer/numbers.test.d.ts +1 -0
- package/dist/tests/lexer/numbers.test.js +23 -0
- package/dist/tests/lexer/numbers.test.js.map +1 -0
- package/dist/tests/lexer/strings.test.d.ts +1 -0
- package/dist/tests/lexer/strings.test.js +72 -0
- package/dist/tests/lexer/strings.test.js.map +1 -0
- package/dist/tests/parser/declarations.test.d.ts +1 -0
- package/dist/tests/parser/declarations.test.js +70 -0
- package/dist/tests/parser/declarations.test.js.map +1 -0
- package/dist/tests/parser/expressions.test.d.ts +1 -0
- package/dist/tests/parser/expressions.test.js +47 -0
- package/dist/tests/parser/expressions.test.js.map +1 -0
- package/dist/tests/parser/helpers.test.d.ts +1 -0
- package/dist/tests/parser/helpers.test.js +58 -0
- package/dist/tests/parser/helpers.test.js.map +1 -0
- package/dist/tests/parser/literals.test.d.ts +1 -0
- package/dist/tests/parser/literals.test.js +70 -0
- package/dist/tests/parser/literals.test.js.map +1 -0
- package/dist/tests/parser/module-syntax.test.d.ts +1 -0
- package/dist/tests/parser/module-syntax.test.js +91 -0
- package/dist/tests/parser/module-syntax.test.js.map +1 -0
- package/dist/tests/parser/statements.test.d.ts +1 -0
- package/dist/tests/parser/statements.test.js +85 -0
- package/dist/tests/parser/statements.test.js.map +1 -0
- package/dist/tests/parser/type-members.test.d.ts +1 -0
- package/dist/tests/parser/type-members.test.js +86 -0
- package/dist/tests/parser/type-members.test.js.map +1 -0
- package/dist/tests/parser/types.test.d.ts +1 -0
- package/dist/tests/parser/types.test.js +143 -0
- package/dist/tests/parser/types.test.js.map +1 -0
- package/package.json +6 -1
- package/runtime/ext_node/fs/fs_ops.c +36 -1
- package/runtime/rt.h +17 -0
- package/runtime/rt_internal.h +88 -2
- package/runtime/xt_builtins.c +174 -6
- package/runtime/xt_containers.c +234 -42
- package/runtime/xt_generator.c +304 -0
- package/runtime/xt_io.c +170 -30
- package/runtime/xt_promise.c +6 -1
- package/runtime/xt_regex.h +105 -35
- package/runtime/xt_stdlib/dispatch.inc +255 -4
- package/runtime/xt_stdlib/strings.inc +17 -13
- package/runtime/xt_stdlib2/arrays.inc +126 -19
- package/runtime/xt_stdlib2/date.inc +9 -2
- package/runtime/xt_stdlib2/error.inc +162 -12
- package/runtime/xt_stdlib2/helpers.inc +4 -0
- package/runtime/xt_stdlib2/json.inc +19 -6
- package/runtime/xt_stdlib2/number-string-statics.inc +1 -1
- package/runtime/xt_stdlib2/numbers.inc +4 -5
- package/runtime/xt_stdlib2/objects.inc +29 -0
- package/runtime/xt_stdlib2/regexp-match.inc +293 -51
- package/runtime/xt_stdlib2/set.inc +58 -2
- package/runtime/xt_stdlib2/strings.inc +38 -0
- package/runtime/xt_symbol.c +149 -0
- package/runtime/xt_values.c +221 -15
- package/scripts/build-runtime.ts +1 -1
- package/src/ast/expressions.ts +8 -0
- package/src/binder/binder.ts +1 -1
- package/src/binder/references.ts +47 -4
- package/src/binder/types.ts +4 -0
- package/src/cli/main.ts +1 -2
- package/src/codegen/generator/calls/access.ts +158 -13
- package/src/codegen/generator/calls/closures.ts +24 -0
- package/src/codegen/generator/calls/invocation.ts +11 -1
- package/src/codegen/generator/calls/literals.ts +10 -4
- package/src/codegen/generator/calls/super.ts +6 -9
- package/src/codegen/generator/context.ts +44 -7
- package/src/codegen/generator/expressions/assignment.ts +1 -3
- package/src/codegen/generator/expressions/primary.ts +108 -2
- package/src/codegen/generator/generator.ts +0 -1
- package/src/codegen/generator/module.ts +24 -18
- package/src/codegen/generator/state.ts +44 -1
- package/src/codegen/generator/statements/control-flow.ts +86 -0
- package/src/codegen/generator/statements/dispatch.ts +18 -6
- package/src/codegen/generator/statements/loops.ts +29 -11
- package/src/codegen/generator/tables.ts +44 -1
- package/src/driver/compiler.ts +32 -2
- package/src/driver/modules.ts +64 -4
- package/src/driver/toolchain.ts +1 -1
- package/src/lexer/scanner.ts +0 -1
- package/src/parser/declarations/classes.ts +4 -0
- package/src/parser/declarations/type-members.ts +0 -2
- package/src/parser/expressions.ts +13 -1
- package/src/parser/helpers.ts +2 -2
- package/src/parser/literals.ts +82 -7
- package/src/parser/modules.ts +0 -1
- package/src/parser/parser.ts +0 -1
- package/src/parser/statements.ts +14 -0
- package/src/parser/types.ts +2 -2
|
@@ -5,13 +5,15 @@
|
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
7
|
SyntaxKind,
|
|
8
|
+
type CallExpression,
|
|
8
9
|
type DeleteExpression,
|
|
9
10
|
type ElementAccessExpression,
|
|
11
|
+
type Expression,
|
|
10
12
|
type Identifier,
|
|
11
13
|
type PropertyAccessExpression,
|
|
12
14
|
} from "../../../ast/nodes.js";
|
|
13
15
|
import { i64, numberLiteral, XT_TRUE, XT_UNDEFINED } from "../../values.js";
|
|
14
|
-
import { MATH_CONSTANTS, NAMESPACE_PROPERTIES } from "../tables.js";
|
|
16
|
+
import { MATH_CONSTANTS, NAMESPACE_PROPERTIES, NUMBER_CONSTANTS } from "../tables.js";
|
|
15
17
|
import type { Generator } from "../generator.js";
|
|
16
18
|
|
|
17
19
|
export interface AccessCallMethods {
|
|
@@ -19,6 +21,14 @@ export interface AccessCallMethods {
|
|
|
19
21
|
emitPropertyAccess(this: Generator, node: PropertyAccessExpression): string;
|
|
20
22
|
emitElementAccess(this: Generator, node: ElementAccessExpression): string;
|
|
21
23
|
emitOptional(this: Generator, objectValue: string, compute: () => string): string;
|
|
24
|
+
/** True when `node` is (part of) an optional chain: a `?.` appears anywhere
|
|
25
|
+
* along its member/call spine. Parentheses end the chain. */
|
|
26
|
+
isOptionalChain(this: Generator, node: Expression): boolean;
|
|
27
|
+
emitOptionalChain(this: Generator, node: Expression): string;
|
|
28
|
+
emitChainValue(this: Generator, node: Expression, endLabel: string): string;
|
|
29
|
+
emitChainReceiver(this: Generator, node: Expression, endLabel: string): string;
|
|
30
|
+
guardOptional(this: Generator, value: string, endLabel: string): void;
|
|
31
|
+
emitPropertyGet(this: Generator, object: string, name: string): string;
|
|
22
32
|
}
|
|
23
33
|
|
|
24
34
|
export const accessCallMethods: AccessCallMethods = {
|
|
@@ -59,6 +69,17 @@ export const accessCallMethods: AccessCallMethods = {
|
|
|
59
69
|
) {
|
|
60
70
|
return numberLiteral(MATH_CONSTANTS[node.name.text]!);
|
|
61
71
|
}
|
|
72
|
+
if (
|
|
73
|
+
node.expression.kind === SyntaxKind.Identifier &&
|
|
74
|
+
(node.expression as Identifier).text === "Number" &&
|
|
75
|
+
NUMBER_CONSTANTS.has(node.name.text) &&
|
|
76
|
+
!this.binding.symbolOfIdentifier.get(node.expression as Identifier)
|
|
77
|
+
) {
|
|
78
|
+
const key = this.stringValue(node.name.text);
|
|
79
|
+
const result = this.reg();
|
|
80
|
+
this.emit(` ${result} = call i64 @xt_number_static(i64 ${key}, i32 0, i64* null)`);
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
62
83
|
if (
|
|
63
84
|
node.expression.kind === SyntaxKind.Identifier &&
|
|
64
85
|
!this.binding.symbolOfIdentifier.get(node.expression as Identifier)
|
|
@@ -86,18 +107,7 @@ export const accessCallMethods: AccessCallMethods = {
|
|
|
86
107
|
}
|
|
87
108
|
}
|
|
88
109
|
const object = this.emitExpression(node.expression);
|
|
89
|
-
|
|
90
|
-
if (node.name.text === "length") {
|
|
91
|
-
const result = this.reg();
|
|
92
|
-
this.emit(` ${result} = call i64 @xt_array_length(i64 ${object})`);
|
|
93
|
-
return result;
|
|
94
|
-
}
|
|
95
|
-
const key = this.stringValue(node.name.text);
|
|
96
|
-
const result = this.reg();
|
|
97
|
-
this.emit(` ${result} = call i64 @xt_get(i64 ${object}, i64 ${key})`);
|
|
98
|
-
return result;
|
|
99
|
-
};
|
|
100
|
-
return node.optional ? this.emitOptional(object, access) : access();
|
|
110
|
+
return this.emitPropertyGet(object, node.name.text);
|
|
101
111
|
},
|
|
102
112
|
|
|
103
113
|
emitElementAccess(node: ElementAccessExpression): string {
|
|
@@ -111,6 +121,141 @@ export const accessCallMethods: AccessCallMethods = {
|
|
|
111
121
|
return node.optional ? this.emitOptional(object, access) : access();
|
|
112
122
|
},
|
|
113
123
|
|
|
124
|
+
/** Property read that works on any receiver value (`length` is special-cased
|
|
125
|
+
* because it is meaningful for arrays and strings). */
|
|
126
|
+
emitPropertyGet(object: string, name: string): string {
|
|
127
|
+
if (name === "length") {
|
|
128
|
+
const result = this.reg();
|
|
129
|
+
this.emit(` ${result} = call i64 @xt_array_length(i64 ${object})`);
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
const key = this.stringValue(name);
|
|
133
|
+
const result = this.reg();
|
|
134
|
+
this.emit(` ${result} = call i64 @xt_get(i64 ${object}, i64 ${key})`);
|
|
135
|
+
return result;
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
/*
|
|
139
|
+
* Optional chaining is a *whole-chain* short circuit in JavaScript:
|
|
140
|
+
* `a?.b.c()` evaluates to `undefined` when `a` is nullish, without reading
|
|
141
|
+
* `.b`/`.c` or calling anything. The parser records `optional` on the
|
|
142
|
+
* individual access node, so we detect the chain here, evaluate it once and
|
|
143
|
+
* branch to a shared end label whenever any `?.` guard fails. The result slot
|
|
144
|
+
* defaults to `undefined` and is overwritten only on the non-short-circuited
|
|
145
|
+
* path.
|
|
146
|
+
*/
|
|
147
|
+
isOptionalChain(node: Expression): boolean {
|
|
148
|
+
let current: Expression = node;
|
|
149
|
+
for (;;) {
|
|
150
|
+
if (current.kind === SyntaxKind.PropertyAccessExpression || current.kind === SyntaxKind.ElementAccessExpression) {
|
|
151
|
+
const access = current as PropertyAccessExpression | ElementAccessExpression;
|
|
152
|
+
if (access.optional) return true;
|
|
153
|
+
current = access.expression;
|
|
154
|
+
} else if (current.kind === SyntaxKind.CallExpression) {
|
|
155
|
+
const call = current as CallExpression;
|
|
156
|
+
if (call.optional) return true;
|
|
157
|
+
current = call.expression;
|
|
158
|
+
} else {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
emitOptionalChain(node: Expression): string {
|
|
165
|
+
const result = this.alloca();
|
|
166
|
+
this.emit(` store i64 ${i64(XT_UNDEFINED)}, i64* ${result}`);
|
|
167
|
+
const endLabel = this.label("optchain.end");
|
|
168
|
+
const value = this.emitChainValue(node, endLabel);
|
|
169
|
+
this.emit(` store i64 ${value}, i64* ${result}`);
|
|
170
|
+
this.terminate(`br label %${endLabel}`);
|
|
171
|
+
this.startBlock(endLabel);
|
|
172
|
+
const merged = this.reg();
|
|
173
|
+
this.emit(` ${merged} = load i64, i64* ${result}`);
|
|
174
|
+
return merged;
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
emitChainValue(node: Expression, endLabel: string): string {
|
|
178
|
+
if (node.kind === SyntaxKind.PropertyAccessExpression) {
|
|
179
|
+
const access = node as PropertyAccessExpression;
|
|
180
|
+
const object = this.emitChainReceiver(access.expression, endLabel);
|
|
181
|
+
if (access.optional) this.guardOptional(object, endLabel);
|
|
182
|
+
return this.emitPropertyGet(object, access.name.text);
|
|
183
|
+
}
|
|
184
|
+
if (node.kind === SyntaxKind.ElementAccessExpression) {
|
|
185
|
+
const access = node as ElementAccessExpression;
|
|
186
|
+
const object = this.emitChainReceiver(access.expression, endLabel);
|
|
187
|
+
if (access.optional) this.guardOptional(object, endLabel);
|
|
188
|
+
const key = this.emitExpression(access.argumentExpression);
|
|
189
|
+
return this.runtimeCall("xt_get", [object, key]);
|
|
190
|
+
}
|
|
191
|
+
if (node.kind === SyntaxKind.CallExpression) {
|
|
192
|
+
const call = node as CallExpression;
|
|
193
|
+
const callee = call.expression;
|
|
194
|
+
/* `a.b?.()` / `a[k]?.()`: evaluate the callee, then invoke it with the
|
|
195
|
+
* receiver as `this` so user methods and first-class built-in method
|
|
196
|
+
* values both bind correctly. */
|
|
197
|
+
if (call.optional) {
|
|
198
|
+
if (callee.kind === SyntaxKind.PropertyAccessExpression || callee.kind === SyntaxKind.ElementAccessExpression) {
|
|
199
|
+
const access = callee as PropertyAccessExpression | ElementAccessExpression;
|
|
200
|
+
const object = this.emitChainReceiver(access.expression, endLabel);
|
|
201
|
+
if (access.optional) this.guardOptional(object, endLabel);
|
|
202
|
+
const calleeValue =
|
|
203
|
+
access.kind === SyntaxKind.PropertyAccessExpression
|
|
204
|
+
? this.emitPropertyGet(object, (access as PropertyAccessExpression).name.text)
|
|
205
|
+
: this.runtimeCall("xt_get", [object, this.emitExpression((access as ElementAccessExpression).argumentExpression)]);
|
|
206
|
+
this.guardOptional(calleeValue, endLabel);
|
|
207
|
+
const args = this.emitArguments(call.arguments);
|
|
208
|
+
const out = this.reg();
|
|
209
|
+
this.emit(` ${out} = call i64 @xt_call_with_this(i64 ${calleeValue}, i64 ${object}, i32 ${args.argc}, i64* ${args.ptr})`);
|
|
210
|
+
return out;
|
|
211
|
+
}
|
|
212
|
+
const calleeValue = this.emitChainReceiver(callee, endLabel);
|
|
213
|
+
this.guardOptional(calleeValue, endLabel);
|
|
214
|
+
const args = this.emitArguments(call.arguments);
|
|
215
|
+
const out = this.reg();
|
|
216
|
+
this.emit(` ${out} = call i64 @xt_closure_call(i64 ${calleeValue}, i32 ${args.argc}, i64* ${args.ptr})`);
|
|
217
|
+
return out;
|
|
218
|
+
}
|
|
219
|
+
/* `a?.b(...)` / `a?.[k](...)`: guard the receiver, then dispatch. */
|
|
220
|
+
if (callee.kind === SyntaxKind.PropertyAccessExpression || callee.kind === SyntaxKind.ElementAccessExpression) {
|
|
221
|
+
const access = callee as PropertyAccessExpression | ElementAccessExpression;
|
|
222
|
+
const object = this.emitChainReceiver(access.expression, endLabel);
|
|
223
|
+
if (access.optional) this.guardOptional(object, endLabel);
|
|
224
|
+
const name =
|
|
225
|
+
access.kind === SyntaxKind.PropertyAccessExpression
|
|
226
|
+
? this.stringValue((access as PropertyAccessExpression).name.text)
|
|
227
|
+
: this.emitExpression((access as ElementAccessExpression).argumentExpression);
|
|
228
|
+
const args = this.emitArguments(call.arguments);
|
|
229
|
+
const out = this.reg();
|
|
230
|
+
this.emit(` ${out} = call i64 @xt_call_method(i64 ${object}, i64 ${name}, i32 ${args.argc}, i64* ${args.ptr})`);
|
|
231
|
+
return out;
|
|
232
|
+
}
|
|
233
|
+
const calleeValue = this.emitChainReceiver(callee, endLabel);
|
|
234
|
+
const args = this.emitArguments(call.arguments);
|
|
235
|
+
const out = this.reg();
|
|
236
|
+
this.emit(` ${out} = call i64 @xt_closure_call(i64 ${calleeValue}, i32 ${args.argc}, i64* ${args.ptr})`);
|
|
237
|
+
return out;
|
|
238
|
+
}
|
|
239
|
+
return this.emitExpression(node);
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
emitChainReceiver(node: Expression, endLabel: string): string {
|
|
243
|
+
return this.isOptionalChain(node) ? this.emitChainValue(node, endLabel) : this.emitExpression(node);
|
|
244
|
+
},
|
|
245
|
+
|
|
246
|
+
/** Branch to `endLabel` (result stays `undefined`) when `value` is nullish. */
|
|
247
|
+
guardOptional(value: string, endLabel: string): void {
|
|
248
|
+
const nullish = this.reg();
|
|
249
|
+
this.emit(` ${nullish} = call i64 @xt_is_nullish(i64 ${value})`);
|
|
250
|
+
const truthy = this.reg();
|
|
251
|
+
this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${nullish})`);
|
|
252
|
+
const condition = this.reg();
|
|
253
|
+
this.emit(` ${condition} = icmp ne i32 ${truthy}, 0`);
|
|
254
|
+
const cont = this.label("optchain.cont");
|
|
255
|
+
this.terminate(`br i1 ${condition}, label %${endLabel}, label %${cont}`);
|
|
256
|
+
this.startBlock(cont);
|
|
257
|
+
},
|
|
258
|
+
|
|
114
259
|
/**
|
|
115
260
|
* Evaluate `object` once and, when it is neither `null` nor `undefined`, run
|
|
116
261
|
* `compute` to produce the value; otherwise short-circuit to `undefined`.
|
|
@@ -10,6 +10,7 @@ import type { Generator } from "../generator.js";
|
|
|
10
10
|
export interface ClosureCallMethods {
|
|
11
11
|
emitClosure(this: Generator, node: ArrowFunction | FunctionExpression): string;
|
|
12
12
|
emitClosureValue(this: Generator, fn: FunctionInfo): string;
|
|
13
|
+
emitFunctionMetadata(this: Generator, closure: string, fn: FunctionInfo): void;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
export const closureCallMethods: ClosureCallMethods = {
|
|
@@ -55,6 +56,29 @@ export const closureCallMethods: ClosureCallMethods = {
|
|
|
55
56
|
this.emit(` ${cast} = bitcast i64 (i64, i64, i32, i64*)* @${this.functionName(fn)} to i8*`);
|
|
56
57
|
const closure = this.reg();
|
|
57
58
|
this.emit(` ${closure} = call i64 @xt_closure_new(i8* ${cast}, i32 ${count}, i64* ${envPtr})`);
|
|
59
|
+
this.emitFunctionMetadata(closure, fn);
|
|
58
60
|
return closure;
|
|
59
61
|
},
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Attach the JavaScript `name` and `length` (arity) of a closure. Arity
|
|
65
|
+
* counts parameters before the first default / rest parameter, matching the
|
|
66
|
+
* specification.
|
|
67
|
+
*/
|
|
68
|
+
emitFunctionMetadata(closure: string, fn: FunctionInfo): void {
|
|
69
|
+
const parameters =
|
|
70
|
+
(fn.node as { parameters?: readonly { initializer?: unknown; dotDotDotToken?: boolean }[] })
|
|
71
|
+
.parameters ?? [];
|
|
72
|
+
let arity = 0;
|
|
73
|
+
for (const parameter of parameters) {
|
|
74
|
+
if (parameter.initializer || parameter.dotDotDotToken) break;
|
|
75
|
+
arity++;
|
|
76
|
+
}
|
|
77
|
+
let name = fn.name;
|
|
78
|
+
if (name === "(anonymous)" || name === "(arrow)") name = "";
|
|
79
|
+
if (fn.isConstructor && fn.classInfo) name = fn.classInfo.name;
|
|
80
|
+
const nameValue = this.stringValue(name);
|
|
81
|
+
this.emit(` call i64 @xt_function_set_metadata(i64 ${closure}, i64 ${nameValue}, i32 ${arity})`);
|
|
82
|
+
if (fn.isGenerator) this.emit(` call i64 @xt_function_set_generator(i64 ${closure})`);
|
|
83
|
+
},
|
|
60
84
|
};
|
|
@@ -100,7 +100,9 @@ export const invocationCallMethods: InvocationCallMethods = {
|
|
|
100
100
|
if (symbol && symbol.kind === SymbolKind.Function) {
|
|
101
101
|
const declaration = symbol.declarations[0];
|
|
102
102
|
const fn = declaration ? this.binding.functionOfNode.get(declaration) : undefined;
|
|
103
|
-
|
|
103
|
+
/* Generator functions are not called directly: the call must create a
|
|
104
|
+
* suspended generator, so fall through to the closure-call path. */
|
|
105
|
+
if (fn && !fn.isGenerator) {
|
|
104
106
|
const args = this.emitArguments(node.arguments);
|
|
105
107
|
const result = this.reg();
|
|
106
108
|
this.emit(
|
|
@@ -204,6 +206,14 @@ export const invocationCallMethods: InvocationCallMethods = {
|
|
|
204
206
|
return result;
|
|
205
207
|
}
|
|
206
208
|
}
|
|
209
|
+
// Modules with no namespace dispatcher (`fs`, `child_process`, ...): lower
|
|
210
|
+
// `import fs from "node:fs"; fs.readFileSync(...)` through the module's
|
|
211
|
+
// named exports, exactly like a named import of `readFileSync`.
|
|
212
|
+
const moduleExports = this.moduleExportsOfSymbol(targetSymbol);
|
|
213
|
+
if (moduleExports) {
|
|
214
|
+
const exported = moduleExports[method];
|
|
215
|
+
if (exported) return this.emitModuleExport(node, exported);
|
|
216
|
+
}
|
|
207
217
|
}
|
|
208
218
|
|
|
209
219
|
void BUILTIN_METHODS;
|
|
@@ -47,14 +47,20 @@ export const literalCallMethods: LiteralCallMethods = {
|
|
|
47
47
|
const nameNode = property.name;
|
|
48
48
|
let key: string;
|
|
49
49
|
if (nameNode.kind === SyntaxKind.ComputedPropertyName) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
this.
|
|
50
|
+
// Property keys may be symbols; `xt_set` normalises with
|
|
51
|
+
// `xt_to_property_key`.
|
|
52
|
+
key = this.emitExpression((nameNode as { expression: Expression }).expression);
|
|
53
53
|
} else {
|
|
54
54
|
key = this.stringValue(propertyNameText(nameNode));
|
|
55
55
|
}
|
|
56
56
|
const value = this.emitExpression(property.initializer);
|
|
57
|
-
|
|
57
|
+
if (property.accessor === "get") {
|
|
58
|
+
this.emit(` call i64 @xt_object_define_getter(i64 ${object}, i64 ${key}, i64 ${value})`);
|
|
59
|
+
} else if (property.accessor === "set") {
|
|
60
|
+
this.emit(` call i64 @xt_object_define_setter(i64 ${object}, i64 ${key}, i64 ${value})`);
|
|
61
|
+
} else {
|
|
62
|
+
this.emit(` call i64 @xt_set(i64 ${object}, i64 ${key}, i64 ${value})`);
|
|
63
|
+
}
|
|
58
64
|
} else if (property.kind === SyntaxKind.ShorthandPropertyAssignment) {
|
|
59
65
|
if (property.initializer) {
|
|
60
66
|
// `{ a = 1 }` is only legal as a destructuring target, not a value.
|
|
@@ -3,9 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
|
-
SyntaxKind,
|
|
7
6
|
type CallExpression,
|
|
8
|
-
type Identifier,
|
|
9
7
|
type PropertyAccessExpression,
|
|
10
8
|
} from "../../../ast/nodes.js";
|
|
11
9
|
import type { Generator } from "../generator.js";
|
|
@@ -20,15 +18,14 @@ export const superCallMethods: SuperCallMethods = {
|
|
|
20
18
|
const thisValue = this.emitThis();
|
|
21
19
|
const classInfo = this.current.fn.classInfo;
|
|
22
20
|
const parent = classInfo?.parentExpression;
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
parent.kind === SyntaxKind.Identifier &&
|
|
26
|
-
(parent as Identifier).text === "Error" &&
|
|
27
|
-
!this.binding.symbolOfIdentifier.get(parent as Identifier);
|
|
28
|
-
if (unboundBuiltin) {
|
|
21
|
+
const errorName = parent ? this.errorFamilyName(parent) : undefined;
|
|
22
|
+
if (errorName) {
|
|
29
23
|
const args = this.emitArguments(node.arguments);
|
|
24
|
+
const nameValue = this.stringValue(errorName);
|
|
30
25
|
const result = this.reg();
|
|
31
|
-
this.emit(
|
|
26
|
+
this.emit(
|
|
27
|
+
` ${result} = call i64 @xt_error_init(i64 ${thisValue}, i64 ${nameValue}, i32 ${args.argc}, i64* ${args.ptr})`,
|
|
28
|
+
);
|
|
32
29
|
return result;
|
|
33
30
|
}
|
|
34
31
|
const proto = this.reg();
|
|
@@ -6,18 +6,19 @@
|
|
|
6
6
|
* helpers every group relies on.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { bind, SymbolKind, type BindResult, type
|
|
9
|
+
import { bind, SymbolKind, type BindResult, type SymbolInfo } from "../../binder/binder.js";
|
|
10
10
|
import type { DiagnosticBag } from "../../diagnostics/diagnostic.js";
|
|
11
11
|
import { DiagnosticCode } from "../../diagnostics/diagnostic.js";
|
|
12
|
-
import { SyntaxKind, type
|
|
12
|
+
import { SyntaxKind, type Identifier, type ImportDeclaration, type Node, type SourceFileNode } from "../../ast/nodes.js";
|
|
13
13
|
import { i64, XT_UNDEFINED } from "../values.js";
|
|
14
14
|
import type {
|
|
15
15
|
BuiltinFunction,
|
|
16
16
|
ExtensionModule,
|
|
17
17
|
ModuleExport,
|
|
18
|
+
ModuleExports,
|
|
18
19
|
} from "../../extensions/registry.js";
|
|
19
20
|
import type { CodegenOptions, FunctionState } from "./state.js";
|
|
20
|
-
import { escapeBytes, kindName, utf8Bytes } from "./tables.js";
|
|
21
|
+
import { escapeBytes, isErrorFamily, kindName, utf8Bytes } from "./tables.js";
|
|
21
22
|
|
|
22
23
|
export class GeneratorContext {
|
|
23
24
|
readonly binding: BindResult;
|
|
@@ -29,6 +30,13 @@ export class GeneratorContext {
|
|
|
29
30
|
readonly importExports = new Map<number, ModuleExport>();
|
|
30
31
|
/** Imported symbol id -> the namespace it aliases (e.g. `path`). */
|
|
31
32
|
readonly importNamespaces = new Map<number, string>();
|
|
33
|
+
/**
|
|
34
|
+
* Imported symbol id -> the named exports of the module it aliases, for
|
|
35
|
+
* modules that do not expose a runtime namespace dispatcher (`fs`,
|
|
36
|
+
* `child_process`, `crypto`, ...). Lets `import fs from "node:fs"` lower
|
|
37
|
+
* `fs.readFileSync(...)` straight to the module's runtime symbol.
|
|
38
|
+
*/
|
|
39
|
+
readonly importModuleExports = new Map<number, ModuleExports>();
|
|
32
40
|
readonly globals: string[] = [];
|
|
33
41
|
readonly functions: string[] = [];
|
|
34
42
|
readonly strings = new Map<string, { label: string; length: number }>();
|
|
@@ -61,9 +69,9 @@ export class GeneratorContext {
|
|
|
61
69
|
if (!module) continue;
|
|
62
70
|
const clause = declaration.importClause;
|
|
63
71
|
if (!clause) continue;
|
|
64
|
-
if (clause.name
|
|
72
|
+
if (clause.name) {
|
|
65
73
|
const symbol = this.binding.symbolOfDeclaration.get(clause.name);
|
|
66
|
-
if (symbol) this.
|
|
74
|
+
if (symbol) this.bindModuleAlias(symbol, module);
|
|
67
75
|
}
|
|
68
76
|
const bindings = clause.namedBindings;
|
|
69
77
|
if (bindings && bindings.kind === SyntaxKind.NamedImports) {
|
|
@@ -73,19 +81,37 @@ export class GeneratorContext {
|
|
|
73
81
|
const symbol = this.binding.symbolOfDeclaration.get(specifier.name);
|
|
74
82
|
if (symbol && exported) this.importExports.set(symbol.id, exported);
|
|
75
83
|
}
|
|
76
|
-
} else if (bindings && bindings.kind === SyntaxKind.NamespaceImport
|
|
84
|
+
} else if (bindings && bindings.kind === SyntaxKind.NamespaceImport) {
|
|
77
85
|
const symbol = this.binding.symbolOfDeclaration.get(bindings.name);
|
|
78
|
-
if (symbol) this.
|
|
86
|
+
if (symbol) this.bindModuleAlias(symbol, module);
|
|
79
87
|
}
|
|
80
88
|
}
|
|
81
89
|
}
|
|
82
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Bind a default/namespace import to either the module's runtime namespace
|
|
93
|
+
* dispatcher (when it has one) or its named exports (when it does not).
|
|
94
|
+
*/
|
|
95
|
+
private bindModuleAlias(symbol: SymbolInfo, module: ExtensionModule): void {
|
|
96
|
+
if (module.namespace) {
|
|
97
|
+
this.importNamespaces.set(symbol.id, module.namespace);
|
|
98
|
+
} else if (module.exports) {
|
|
99
|
+
this.importModuleExports.set(symbol.id, module.exports);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
83
103
|
/** Namespace name an imported alias refers to, if any. */
|
|
84
104
|
namespaceOfSymbol(symbol: SymbolInfo | undefined): string | undefined {
|
|
85
105
|
if (!symbol || symbol.kind !== SymbolKind.Import) return undefined;
|
|
86
106
|
return this.importNamespaces.get(symbol.id);
|
|
87
107
|
}
|
|
88
108
|
|
|
109
|
+
/** Named exports a default/namespace import aliases, if any. */
|
|
110
|
+
moduleExportsOfSymbol(symbol: SymbolInfo | undefined): ModuleExports | undefined {
|
|
111
|
+
if (!symbol || symbol.kind !== SymbolKind.Import) return undefined;
|
|
112
|
+
return this.importModuleExports.get(symbol.id);
|
|
113
|
+
}
|
|
114
|
+
|
|
89
115
|
// -- emission primitives -------------------------------------------------
|
|
90
116
|
|
|
91
117
|
emit(line: string): void {
|
|
@@ -210,6 +236,17 @@ export class GeneratorContext {
|
|
|
210
236
|
|
|
211
237
|
// -- string pool ---------------------------------------------------------
|
|
212
238
|
|
|
239
|
+
/**
|
|
240
|
+
* The name of the builtin Error-family constructor an expression refers to
|
|
241
|
+
* (`extends TypeError`), or `undefined` for a user class / shadowed name.
|
|
242
|
+
*/
|
|
243
|
+
errorFamilyName(expression: Node): string | undefined {
|
|
244
|
+
if (expression.kind !== SyntaxKind.Identifier) return undefined;
|
|
245
|
+
const identifier = expression as Identifier;
|
|
246
|
+
if (this.binding.symbolOfIdentifier.get(identifier)) return undefined;
|
|
247
|
+
return isErrorFamily(identifier.text) ? identifier.text : undefined;
|
|
248
|
+
}
|
|
249
|
+
|
|
213
250
|
stringValue(text: string): string {
|
|
214
251
|
const entry = this.internString(text);
|
|
215
252
|
const result = this.reg();
|
|
@@ -196,9 +196,7 @@ export const assignmentExpressionMethods: AssignmentExpressionMethods = {
|
|
|
196
196
|
const assignment = property as PropertyAssignment;
|
|
197
197
|
let key: string;
|
|
198
198
|
if (assignment.name.kind === SyntaxKind.ComputedPropertyName) {
|
|
199
|
-
|
|
200
|
-
key = this.reg();
|
|
201
|
-
this.emit(` ${key} = call i64 @xt_to_string(i64 ${keyValue})`);
|
|
199
|
+
key = this.emitExpression((assignment.name as { expression: Expression }).expression);
|
|
202
200
|
} else {
|
|
203
201
|
key = this.stringValue(propertyNameText(assignment.name));
|
|
204
202
|
}
|
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
type AwaitExpression,
|
|
10
10
|
type BinaryExpression,
|
|
11
11
|
type CallExpression,
|
|
12
|
-
type ClassExpression,
|
|
13
12
|
type ConditionalExpression,
|
|
14
13
|
type DeleteExpression,
|
|
15
14
|
type ElementAccessExpression,
|
|
@@ -21,13 +20,15 @@ import {
|
|
|
21
20
|
type PostfixUnaryExpression,
|
|
22
21
|
type PrefixUnaryExpression,
|
|
23
22
|
type PropertyAccessExpression,
|
|
23
|
+
type TaggedTemplateExpression,
|
|
24
24
|
type TemplateLiteral,
|
|
25
|
+
type YieldExpression,
|
|
25
26
|
type ArrayLiteralExpression,
|
|
26
27
|
} from "../../../ast/nodes.js";
|
|
27
28
|
import { SymbolKind, type SymbolInfo } from "../../../binder/binder.js";
|
|
28
29
|
import { DiagnosticCode } from "../../../diagnostics/diagnostic.js";
|
|
29
30
|
import { i64, numberLiteral, XT_FALSE, XT_NULL, XT_TRUE, XT_UNDEFINED } from "../../values.js";
|
|
30
|
-
import { BUILTIN_FUNCTION_VALUES, CTOR_FUNCTIONS } from "../tables.js";
|
|
31
|
+
import { BUILTIN_FUNCTION_VALUES, CTOR_FUNCTIONS, ERROR_CONSTRUCTORS } from "../tables.js";
|
|
31
32
|
import type { Generator } from "../generator.js";
|
|
32
33
|
|
|
33
34
|
export interface PrimaryExpressionMethods {
|
|
@@ -35,13 +36,18 @@ export interface PrimaryExpressionMethods {
|
|
|
35
36
|
emitIdentifier(this: Generator, identifier: Identifier): string;
|
|
36
37
|
emitFunctionValue(this: Generator, symbol: SymbolInfo): string;
|
|
37
38
|
emitTemplate(this: Generator, node: TemplateLiteral): string;
|
|
39
|
+
emitTaggedTemplate(this: Generator, node: TaggedTemplateExpression): string;
|
|
38
40
|
emitThis(this: Generator): string;
|
|
39
41
|
emitNew(this: Generator, node: NewExpression): string;
|
|
40
42
|
emitAwait(this: Generator, node: AwaitExpression): string;
|
|
43
|
+
emitYield(this: Generator, node: YieldExpression): string;
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
export const primaryExpressionMethods: PrimaryExpressionMethods = {
|
|
44
47
|
emitExpression(node: Expression): string {
|
|
48
|
+
/* An optional chain must be lowered as a unit so `?.` short-circuits the
|
|
49
|
+
entire chain (`a?.b.c()`), not just the guarded member. */
|
|
50
|
+
if (this.isOptionalChain(node)) return this.emitOptionalChain(node);
|
|
45
51
|
switch (node.kind) {
|
|
46
52
|
case SyntaxKind.Identifier:
|
|
47
53
|
return this.emitIdentifier(node as Identifier);
|
|
@@ -77,6 +83,8 @@ export const primaryExpressionMethods: PrimaryExpressionMethods = {
|
|
|
77
83
|
}
|
|
78
84
|
case SyntaxKind.TemplateLiteral:
|
|
79
85
|
return this.emitTemplate(node as TemplateLiteral);
|
|
86
|
+
case SyntaxKind.TaggedTemplateExpression:
|
|
87
|
+
return this.emitTaggedTemplate(node as TaggedTemplateExpression);
|
|
80
88
|
case SyntaxKind.TrueKeyword:
|
|
81
89
|
return i64(XT_TRUE);
|
|
82
90
|
case SyntaxKind.FalseKeyword:
|
|
@@ -108,6 +116,8 @@ export const primaryExpressionMethods: PrimaryExpressionMethods = {
|
|
|
108
116
|
return this.emitNew(node as NewExpression);
|
|
109
117
|
case SyntaxKind.AwaitExpression:
|
|
110
118
|
return this.emitAwait(node as AwaitExpression);
|
|
119
|
+
case SyntaxKind.YieldExpression:
|
|
120
|
+
return this.emitYield(node as YieldExpression);
|
|
111
121
|
case SyntaxKind.ClassExpression: {
|
|
112
122
|
const info = this.binding.classOfNode.get(node);
|
|
113
123
|
if (!info) {
|
|
@@ -198,6 +208,13 @@ export const primaryExpressionMethods: PrimaryExpressionMethods = {
|
|
|
198
208
|
this.emit(` ${closure} = call i64 @xt_closure_new(i8* ${cast}, i32 0, i64* null)`);
|
|
199
209
|
return closure;
|
|
200
210
|
}
|
|
211
|
+
const errorCtor = ERROR_CONSTRUCTORS[identifier.text];
|
|
212
|
+
if (errorCtor) {
|
|
213
|
+
// A first-class Error-family constructor (`instanceof TypeError`,
|
|
214
|
+
// `typeof RangeError`, `class X extends Error`, ...).
|
|
215
|
+
const nameValue = this.stringValue(identifier.text);
|
|
216
|
+
return this.runtimeCall("xt_error_constructor", [nameValue]);
|
|
217
|
+
}
|
|
201
218
|
this.diagnostics.error(
|
|
202
219
|
DiagnosticCode.CannotFindName,
|
|
203
220
|
`Cannot find name '${identifier.text}'`,
|
|
@@ -217,6 +234,7 @@ export const primaryExpressionMethods: PrimaryExpressionMethods = {
|
|
|
217
234
|
this.emit(` ${cast} = bitcast i64 (i64, i64, i32, i64*)* @${this.functionName(fn)} to i8*`);
|
|
218
235
|
const closure = this.reg();
|
|
219
236
|
this.emit(` ${closure} = call i64 @xt_closure_new(i8* ${cast}, i32 0, i64* null)`);
|
|
237
|
+
this.emitFunctionMetadata(closure, fn);
|
|
220
238
|
return closure;
|
|
221
239
|
},
|
|
222
240
|
|
|
@@ -234,6 +252,19 @@ export const primaryExpressionMethods: PrimaryExpressionMethods = {
|
|
|
234
252
|
return result;
|
|
235
253
|
},
|
|
236
254
|
|
|
255
|
+
emitYield(node: YieldExpression): string {
|
|
256
|
+
if (node.delegate) {
|
|
257
|
+
const delegate = this.emitExpression(node.expression as Expression);
|
|
258
|
+
const result = this.reg();
|
|
259
|
+
this.emit(` ${result} = call i64 @xt_yield_star(i64 ${delegate})`);
|
|
260
|
+
return result;
|
|
261
|
+
}
|
|
262
|
+
const value = node.expression ? this.emitExpression(node.expression) : i64(XT_UNDEFINED);
|
|
263
|
+
const result = this.reg();
|
|
264
|
+
this.emit(` ${result} = call i64 @xt_yield(i64 ${value})`);
|
|
265
|
+
return result;
|
|
266
|
+
},
|
|
267
|
+
|
|
237
268
|
emitNew(node: NewExpression): string {
|
|
238
269
|
const callee = node.expression;
|
|
239
270
|
if (callee.kind === SyntaxKind.Identifier) {
|
|
@@ -301,4 +332,79 @@ export const primaryExpressionMethods: PrimaryExpressionMethods = {
|
|
|
301
332
|
}
|
|
302
333
|
return accumulator;
|
|
303
334
|
},
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Lower a tagged template: build the (cooked) strings array with a `.raw`
|
|
338
|
+
* sibling and call the tag with it followed by the interpolation values.
|
|
339
|
+
* `String.raw` is the one namespace tag special-cased by the runtime.
|
|
340
|
+
*/
|
|
341
|
+
emitTaggedTemplate(node: TaggedTemplateExpression): string {
|
|
342
|
+
const template = node.template;
|
|
343
|
+
let cooked: string[];
|
|
344
|
+
let raw: string[];
|
|
345
|
+
let expressions: readonly Expression[];
|
|
346
|
+
if (template.kind === SyntaxKind.NoSubstitutionTemplateLiteral) {
|
|
347
|
+
cooked = [template.value];
|
|
348
|
+
raw = [template.raw ?? template.value];
|
|
349
|
+
expressions = [];
|
|
350
|
+
} else {
|
|
351
|
+
const literal = template as TemplateLiteral;
|
|
352
|
+
cooked = [literal.head, ...literal.spans.map((span) => span.literal)];
|
|
353
|
+
raw = [literal.raw ?? literal.head, ...literal.spans.map((span) => span.raw ?? span.literal)];
|
|
354
|
+
expressions = literal.spans.map((span) => span.expression);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const makeArray = (items: string[]): string => {
|
|
358
|
+
const array = this.reg();
|
|
359
|
+
this.emit(` ${array} = call i64 @xt_array_new(i32 0, i64* null)`);
|
|
360
|
+
for (const item of items) {
|
|
361
|
+
const value = this.stringValue(item);
|
|
362
|
+
this.emit(` call i64 @xt_array_push(i64 ${array}, i64 ${value})`);
|
|
363
|
+
}
|
|
364
|
+
return array;
|
|
365
|
+
};
|
|
366
|
+
const strings = makeArray(cooked);
|
|
367
|
+
const rawArray = makeArray(raw);
|
|
368
|
+
const rawKey = this.stringValue("raw");
|
|
369
|
+
this.emit(` call i64 @xt_set(i64 ${strings}, i64 ${rawKey}, i64 ${rawArray})`);
|
|
370
|
+
|
|
371
|
+
const argc = String(expressions.length + 1);
|
|
372
|
+
const ptr = `%args${this.current.allocas.length}`;
|
|
373
|
+
this.current.allocas.push(`${ptr} = alloca i64, i32 ${expressions.length + 1}`);
|
|
374
|
+
const first = this.reg();
|
|
375
|
+
this.emit(` ${first} = getelementptr i64, i64* ${ptr}, i32 0`);
|
|
376
|
+
this.emit(` store i64 ${strings}, i64* ${first}`);
|
|
377
|
+
for (let index = 0; index < expressions.length; index++) {
|
|
378
|
+
const value = this.emitExpression(expressions[index]!);
|
|
379
|
+
const slot = this.reg();
|
|
380
|
+
this.emit(` ${slot} = getelementptr i64, i64* ${ptr}, i32 ${index + 1}`);
|
|
381
|
+
this.emit(` store i64 ${value}, i64* ${slot}`);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const tag = node.tag;
|
|
385
|
+
if (tag.kind === SyntaxKind.PropertyAccessExpression) {
|
|
386
|
+
const access = tag as PropertyAccessExpression;
|
|
387
|
+
const owner = access.expression;
|
|
388
|
+
if (
|
|
389
|
+
owner.kind === SyntaxKind.Identifier &&
|
|
390
|
+
(owner as Identifier).text === "String" &&
|
|
391
|
+
access.name.text === "raw" &&
|
|
392
|
+
!this.binding.symbolOfIdentifier.get(owner as Identifier)
|
|
393
|
+
) {
|
|
394
|
+
const key = this.stringValue("raw");
|
|
395
|
+
const result = this.reg();
|
|
396
|
+
this.emit(` ${result} = call i64 @xt_string_static(i64 ${key}, i32 ${argc}, i64* ${ptr})`);
|
|
397
|
+
return result;
|
|
398
|
+
}
|
|
399
|
+
const object = this.emitExpression(owner);
|
|
400
|
+
const name = this.stringValue(access.name.text);
|
|
401
|
+
const result = this.reg();
|
|
402
|
+
this.emit(` ${result} = call i64 @xt_call_method(i64 ${object}, i64 ${name}, i32 ${argc}, i64* ${ptr})`);
|
|
403
|
+
return result;
|
|
404
|
+
}
|
|
405
|
+
const tagValue = this.emitExpression(tag);
|
|
406
|
+
const result = this.reg();
|
|
407
|
+
this.emit(` ${result} = call i64 @xt_closure_call(i64 ${tagValue}, i32 ${argc}, i64* ${ptr})`);
|
|
408
|
+
return result;
|
|
409
|
+
},
|
|
304
410
|
};
|
|
@@ -18,7 +18,6 @@ import type { CodegenOptions, CodegenResult } from "./state.js";
|
|
|
18
18
|
|
|
19
19
|
export class Generator extends GeneratorContext {}
|
|
20
20
|
|
|
21
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
22
21
|
export interface Generator extends ModuleMethods, StatementMethods, ExpressionMethods, CallMethods {}
|
|
23
22
|
|
|
24
23
|
Object.assign(Generator.prototype, moduleMethods, statementMethods, expressionMethods, callMethods);
|