xbintsc 0.2.8 → 0.3.1
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/README.md +17 -2
- package/README.zh-CN.md +16 -2
- package/bin/xbintsc.js +49 -10
- package/dist/src/ast/expressions.d.ts +2 -0
- package/dist/src/binder/binder.d.ts +24 -98
- package/dist/src/binder/binder.js +13 -559
- package/dist/src/binder/binder.js.map +1 -1
- package/dist/src/binder/declarations.d.ts +16 -0
- package/dist/src/binder/declarations.js +168 -0
- package/dist/src/binder/declarations.js.map +1 -0
- package/dist/src/binder/helpers.d.ts +11 -0
- package/dist/src/binder/helpers.js +46 -0
- package/dist/src/binder/helpers.js.map +1 -0
- package/dist/src/binder/references.d.ts +21 -0
- package/dist/src/binder/references.js +349 -0
- package/dist/src/binder/references.js.map +1 -0
- package/dist/src/binder/types.d.ts +114 -0
- package/dist/src/binder/types.js +27 -0
- package/dist/src/binder/types.js.map +1 -0
- package/dist/src/cli/main.d.ts +1 -0
- package/dist/src/cli/main.js +40 -0
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/codegen/generator/calls/access.d.ts +13 -0
- package/dist/src/codegen/generator/calls/access.js +115 -0
- package/dist/src/codegen/generator/calls/access.js.map +1 -0
- package/dist/src/codegen/generator/calls/closures.d.ts +11 -0
- package/dist/src/codegen/generator/calls/closures.js +50 -0
- package/dist/src/codegen/generator/calls/closures.js.map +1 -0
- package/dist/src/codegen/generator/calls/invocation.d.ts +18 -0
- package/dist/src/codegen/generator/calls/invocation.js +242 -0
- package/dist/src/codegen/generator/calls/invocation.js.map +1 -0
- package/dist/src/codegen/generator/calls/literals.d.ts +10 -0
- package/dist/src/codegen/generator/calls/literals.js +69 -0
- package/dist/src/codegen/generator/calls/literals.js.map +1 -0
- package/dist/src/codegen/generator/calls/super.d.ts +10 -0
- package/dist/src/codegen/generator/calls/super.js +47 -0
- package/dist/src/codegen/generator/calls/super.js.map +1 -0
- package/dist/src/codegen/generator/calls.d.ts +15 -24
- package/dist/src/codegen/generator/calls.js +15 -482
- package/dist/src/codegen/generator/calls.js.map +1 -1
- package/dist/src/codegen/generator/context.d.ts +1 -1
- package/dist/src/codegen/generator/context.js +2 -2
- package/dist/src/codegen/generator/context.js.map +1 -1
- package/dist/src/codegen/generator/expressions/assignment.d.ts +13 -0
- package/dist/src/codegen/generator/expressions/assignment.js +203 -0
- package/dist/src/codegen/generator/expressions/assignment.js.map +1 -0
- package/dist/src/codegen/generator/expressions/operators.d.ts +14 -0
- package/dist/src/codegen/generator/expressions/operators.js +163 -0
- package/dist/src/codegen/generator/expressions/operators.js.map +1 -0
- package/dist/src/codegen/generator/expressions/primary.d.ts +17 -0
- package/dist/src/codegen/generator/expressions/primary.js +260 -0
- package/dist/src/codegen/generator/expressions/primary.js.map +1 -0
- package/dist/src/codegen/generator/expressions.d.ts +11 -21
- package/dist/src/codegen/generator/expressions.js +11 -494
- package/dist/src/codegen/generator/expressions.js.map +1 -1
- package/dist/src/codegen/generator/module.js +16 -4
- package/dist/src/codegen/generator/module.js.map +1 -1
- package/dist/src/codegen/generator/state.js +8 -0
- package/dist/src/codegen/generator/state.js.map +1 -1
- package/dist/src/codegen/generator/statements/control-flow.d.ts +14 -0
- package/dist/src/codegen/generator/statements/control-flow.js +199 -0
- package/dist/src/codegen/generator/statements/control-flow.js.map +1 -0
- package/dist/src/codegen/generator/statements/dispatch.d.ts +12 -0
- package/dist/src/codegen/generator/statements/dispatch.js +100 -0
- package/dist/src/codegen/generator/statements/dispatch.js.map +1 -0
- package/dist/src/codegen/generator/statements/loops.d.ts +13 -0
- package/dist/src/codegen/generator/statements/loops.js +152 -0
- package/dist/src/codegen/generator/statements/loops.js.map +1 -0
- package/dist/src/codegen/generator/statements/variables.d.ts +13 -0
- package/dist/src/codegen/generator/statements/variables.js +146 -0
- package/dist/src/codegen/generator/statements/variables.js.map +1 -0
- package/dist/src/codegen/generator/statements.d.ts +13 -21
- package/dist/src/codegen/generator/statements.js +13 -568
- package/dist/src/codegen/generator/statements.js.map +1 -1
- package/dist/src/codegen/generator/tables.js +7 -0
- package/dist/src/codegen/generator/tables.js.map +1 -1
- package/dist/src/codegen/values.d.ts +17 -17
- package/dist/src/codegen/values.js +21 -57
- package/dist/src/codegen/values.js.map +1 -1
- package/dist/src/diagnostics/diagnostic.d.ts +1 -0
- package/dist/src/diagnostics/diagnostic.js +1 -0
- package/dist/src/diagnostics/diagnostic.js.map +1 -1
- package/dist/src/diagnostics/source.d.ts +1 -1
- package/dist/src/diagnostics/source.js +7 -12
- package/dist/src/diagnostics/source.js.map +1 -1
- package/dist/src/driver/compiler.d.ts +4 -0
- package/dist/src/driver/compiler.js +51 -16
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/index.d.ts +5 -2
- package/dist/src/driver/index.js +5 -2
- package/dist/src/driver/index.js.map +1 -1
- package/dist/src/driver/paths.d.ts +20 -0
- package/dist/src/driver/paths.js +49 -0
- package/dist/src/driver/paths.js.map +1 -1
- package/dist/src/driver/runtime-lib.d.ts +18 -0
- package/dist/src/driver/runtime-lib.js +31 -0
- package/dist/src/driver/runtime-lib.js.map +1 -0
- package/dist/src/driver/toolchain-download.d.ts +50 -0
- package/dist/src/driver/toolchain-download.js +76 -0
- package/dist/src/driver/toolchain-download.js.map +1 -0
- package/dist/src/driver/toolchain-provider.d.ts +33 -0
- package/dist/src/driver/toolchain-provider.js +71 -0
- package/dist/src/driver/toolchain-provider.js.map +1 -0
- package/dist/src/driver/toolchain.d.ts +9 -4
- package/dist/src/driver/toolchain.js +6 -10
- package/dist/src/driver/toolchain.js.map +1 -1
- package/dist/src/extensions/node/child_process/index.d.ts +4 -2
- package/dist/src/extensions/node/child_process/index.js +4 -2
- package/dist/src/extensions/node/child_process/index.js.map +1 -1
- package/dist/src/extensions/node/events/index.d.ts +10 -0
- package/dist/src/extensions/node/events/index.js +22 -0
- package/dist/src/extensions/node/events/index.js.map +1 -0
- package/dist/src/extensions/node/index.js +6 -0
- package/dist/src/extensions/node/index.js.map +1 -1
- package/dist/src/extensions/node/querystring/index.d.ts +10 -0
- package/dist/src/extensions/node/querystring/index.js +25 -0
- package/dist/src/extensions/node/querystring/index.js.map +1 -0
- package/dist/src/extensions/node/util/index.d.ts +11 -0
- package/dist/src/extensions/node/util/index.js +41 -0
- package/dist/src/extensions/node/util/index.js.map +1 -0
- package/dist/src/extensions/registry.d.ts +6 -0
- package/dist/src/extensions/registry.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/lexer/scanner/identifiers.d.ts +9 -0
- package/dist/src/lexer/scanner/identifiers.js +33 -0
- package/dist/src/lexer/scanner/identifiers.js.map +1 -0
- package/dist/src/lexer/scanner/numbers.d.ts +10 -0
- package/dist/src/lexer/scanner/numbers.js +89 -0
- package/dist/src/lexer/scanner/numbers.js.map +1 -0
- package/dist/src/lexer/scanner/punctuation.d.ts +9 -0
- package/dist/src/lexer/scanner/punctuation.js +151 -0
- package/dist/src/lexer/scanner/punctuation.js.map +1 -0
- package/dist/src/lexer/scanner/regex.d.ts +11 -0
- package/dist/src/lexer/scanner/regex.js +69 -0
- package/dist/src/lexer/scanner/regex.js.map +1 -0
- package/dist/src/lexer/scanner/strings.d.ts +13 -0
- package/dist/src/lexer/scanner/strings.js +118 -0
- package/dist/src/lexer/scanner/strings.js.map +1 -0
- package/dist/src/lexer/scanner.d.ts +20 -27
- package/dist/src/lexer/scanner.js +11 -415
- package/dist/src/lexer/scanner.js.map +1 -1
- package/dist/src/parser/declarations/classes.d.ts +14 -0
- package/dist/src/parser/declarations/classes.js +191 -0
- package/dist/src/parser/declarations/classes.js.map +1 -0
- package/dist/src/parser/declarations/functions.d.ts +17 -0
- package/dist/src/parser/declarations/functions.js +241 -0
- package/dist/src/parser/declarations/functions.js.map +1 -0
- package/dist/src/parser/declarations/type-members.d.ts +16 -0
- package/dist/src/parser/declarations/type-members.js +156 -0
- package/dist/src/parser/declarations/type-members.js.map +1 -0
- package/dist/src/parser/declarations.d.ts +13 -24
- package/dist/src/parser/declarations.js +13 -571
- package/dist/src/parser/declarations.js.map +1 -1
- package/dist/src/parser/helpers.js +13 -4
- package/dist/src/parser/helpers.js.map +1 -1
- package/dist/src/parser/literals.js +10 -1
- package/dist/src/parser/literals.js.map +1 -1
- package/dist/tests/codegen/llvm.test.js +24 -0
- package/dist/tests/codegen/llvm.test.js.map +1 -1
- package/dist/tests/driver/compiler.test.js +3 -0
- package/dist/tests/driver/compiler.test.js.map +1 -1
- package/dist/tests/driver/paths.test.d.ts +1 -0
- package/dist/tests/driver/paths.test.js +26 -0
- package/dist/tests/driver/paths.test.js.map +1 -0
- package/dist/tests/driver/toolchain-download.test.d.ts +1 -0
- package/dist/tests/driver/toolchain-download.test.js +38 -0
- package/dist/tests/driver/toolchain-download.test.js.map +1 -0
- package/dist/tests/driver/toolchain-provider.test.d.ts +1 -0
- package/dist/tests/driver/toolchain-provider.test.js +37 -0
- package/dist/tests/driver/toolchain-provider.test.js.map +1 -0
- package/dist/tests/e2e/compile-node.test.d.ts +7 -0
- package/dist/tests/e2e/compile-node.test.js +123 -0
- package/dist/tests/e2e/compile-node.test.js.map +1 -0
- package/dist/tests/e2e/compile.test.d.ts +4 -1
- package/dist/tests/e2e/compile.test.js +90 -130
- package/dist/tests/e2e/compile.test.js.map +1 -1
- package/dist/tests/e2e/harness.d.ts +36 -0
- package/dist/tests/e2e/harness.js +64 -0
- package/dist/tests/e2e/harness.js.map +1 -0
- package/dist/tests/e2e/node-modules.test.js +60 -0
- package/dist/tests/e2e/node-modules.test.js.map +1 -1
- package/dist/tests/extensions/registry.test.js +4 -0
- package/dist/tests/extensions/registry.test.js.map +1 -1
- package/package.json +5 -3
- package/runtime/ext_node/buffer/buffer.c +12 -552
- package/runtime/ext_node/buffer/parts/byte-access.inc +35 -0
- package/runtime/ext_node/buffer/parts/construction.inc +60 -0
- package/runtime/ext_node/buffer/parts/integer-io.inc +119 -0
- package/runtime/ext_node/buffer/parts/methods.inc +135 -0
- package/runtime/ext_node/buffer/parts/prototype.inc +54 -0
- package/runtime/ext_node/buffer/parts/search.inc +86 -0
- package/runtime/ext_node/buffer/parts/static-api.inc +58 -0
- package/runtime/ext_node/child_process/child_process.c +37 -3
- package/runtime/ext_node/events/events.c +285 -0
- package/runtime/ext_node/http/http.c +11 -736
- package/runtime/ext_node/http/parts/client.inc +290 -0
- package/runtime/ext_node/http/parts/helpers.inc +45 -0
- package/runtime/ext_node/http/parts/prototypes.inc +57 -0
- package/runtime/ext_node/http/parts/response.inc +149 -0
- package/runtime/ext_node/http/parts/server.inc +133 -0
- package/runtime/ext_node/http/parts/string-builder.inc +58 -0
- package/runtime/ext_node/net/net.c +12 -504
- package/runtime/ext_node/net/parts/construction.inc +22 -0
- package/runtime/ext_node/net/parts/platform.inc +60 -0
- package/runtime/ext_node/net/parts/prototypes.inc +35 -0
- package/runtime/ext_node/net/parts/read-loop.inc +58 -0
- package/runtime/ext_node/net/parts/server.inc +131 -0
- package/runtime/ext_node/net/parts/socket-methods.inc +100 -0
- package/runtime/ext_node/net/parts/static-api.inc +93 -0
- package/runtime/ext_node/querystring/querystring.c +251 -0
- package/runtime/ext_node/util/util.c +456 -0
- package/runtime/rt.h +8 -2
- package/runtime/rt_internal.h +34 -0
- package/runtime/xt_bigint/arithmetic.inc +135 -0
- package/runtime/xt_bigint/bits.inc +98 -0
- package/runtime/xt_bigint/bitwise.inc +103 -0
- package/runtime/xt_bigint/compare.inc +30 -0
- package/runtime/xt_bigint/conversions.inc +51 -0
- package/runtime/xt_bigint/division.inc +85 -0
- package/runtime/xt_bigint/format.inc +145 -0
- package/runtime/xt_bigint/helpers.inc +92 -0
- package/runtime/xt_bigint/magnitude.inc +88 -0
- package/runtime/xt_bigint/public.inc +59 -0
- package/runtime/xt_bigint/small-arithmetic.inc +58 -0
- package/runtime/xt_bigint.c +42 -0
- package/runtime/xt_builtins.c +40 -5
- package/runtime/xt_io.c +7 -0
- package/runtime/xt_stdlib/arrays.inc +161 -0
- package/runtime/xt_stdlib/dispatch.inc +116 -0
- package/runtime/xt_stdlib/helpers.inc +23 -0
- package/runtime/xt_stdlib/math.inc +72 -0
- package/runtime/xt_stdlib/strings.inc +178 -0
- package/runtime/xt_stdlib.c +10 -549
- package/runtime/xt_stdlib2/array-statics.inc +37 -0
- package/runtime/xt_stdlib2/arrays.inc +189 -0
- package/runtime/xt_stdlib2/container.inc +23 -0
- package/runtime/xt_stdlib2/date.inc +72 -0
- package/runtime/xt_stdlib2/error.inc +22 -0
- package/runtime/xt_stdlib2/helpers.inc +43 -0
- package/runtime/xt_stdlib2/json.inc +213 -0
- package/runtime/xt_stdlib2/map.inc +95 -0
- package/runtime/xt_stdlib2/number-string-statics.inc +73 -0
- package/runtime/xt_stdlib2/numbers.inc +38 -0
- package/runtime/xt_stdlib2/objects.inc +41 -0
- package/runtime/xt_stdlib2/regexp-match.inc +204 -0
- package/runtime/xt_stdlib2/regexp.inc +20 -0
- package/runtime/xt_stdlib2/set.inc +105 -0
- package/runtime/xt_stdlib2/strings.inc +94 -0
- package/runtime/xt_stdlib2.c +19 -1278
- package/runtime/xt_values.c +155 -12
- package/scripts/build-runtime.ts +109 -0
- package/scripts/fetch-toolchain.ts +101 -0
- package/scripts/package-release.ts +192 -0
- package/src/ast/expressions.ts +2 -0
- package/src/binder/binder.ts +40 -725
- package/src/binder/declarations.ts +203 -0
- package/src/binder/helpers.ts +47 -0
- package/src/binder/references.ts +410 -0
- package/src/binder/types.ts +134 -0
- package/src/cli/main.ts +43 -0
- package/src/codegen/generator/calls/access.ts +139 -0
- package/src/codegen/generator/calls/closures.ts +60 -0
- package/src/codegen/generator/calls/invocation.ts +282 -0
- package/src/codegen/generator/calls/literals.ts +77 -0
- package/src/codegen/generator/calls/super.ts +61 -0
- package/src/codegen/generator/calls.ts +30 -552
- package/src/codegen/generator/context.ts +2 -2
- package/src/codegen/generator/expressions/assignment.ts +226 -0
- package/src/codegen/generator/expressions/operators.ts +182 -0
- package/src/codegen/generator/expressions/primary.ts +304 -0
- package/src/codegen/generator/expressions.ts +22 -553
- package/src/codegen/generator/module.ts +14 -4
- package/src/codegen/generator/state.ts +8 -0
- package/src/codegen/generator/statements/control-flow.ts +225 -0
- package/src/codegen/generator/statements/dispatch.ts +122 -0
- package/src/codegen/generator/statements/loops.ts +173 -0
- package/src/codegen/generator/statements/variables.ts +152 -0
- package/src/codegen/generator/statements.ts +26 -612
- package/src/codegen/generator/tables.ts +7 -0
- package/src/codegen/values.ts +22 -57
- package/src/diagnostics/diagnostic.ts +1 -0
- package/src/diagnostics/source.ts +7 -12
- package/src/driver/compiler.ts +61 -18
- package/src/driver/index.ts +17 -2
- package/src/driver/paths.ts +54 -0
- package/src/driver/runtime-lib.ts +32 -0
- package/src/driver/toolchain-download.ts +108 -0
- package/src/driver/toolchain-provider.ts +91 -0
- package/src/driver/toolchain.ts +29 -11
- package/src/extensions/node/child_process/index.ts +4 -2
- package/src/extensions/node/events/index.ts +27 -0
- package/src/extensions/node/index.ts +6 -0
- package/src/extensions/node/querystring/index.ts +29 -0
- package/src/extensions/node/util/index.ts +48 -0
- package/src/extensions/registry.ts +6 -0
- package/src/index.ts +1 -0
- package/src/lexer/scanner/identifiers.ts +37 -0
- package/src/lexer/scanner/numbers.ts +84 -0
- package/src/lexer/scanner/punctuation.ts +132 -0
- package/src/lexer/scanner/regex.ts +79 -0
- package/src/lexer/scanner/strings.ts +128 -0
- package/src/lexer/scanner.ts +35 -401
- package/src/parser/declarations/classes.ts +215 -0
- package/src/parser/declarations/functions.ts +254 -0
- package/src/parser/declarations/type-members.ts +181 -0
- package/src/parser/declarations.ts +32 -609
- package/src/parser/helpers.ts +13 -6
- package/src/parser/literals.ts +10 -1
- package/scripts/build-runtime.mjs +0 -55
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Control-flow lowering: `if`, `switch`, `try`/`catch`/`finally` and `return`.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
SyntaxKind,
|
|
7
|
+
type CaseClause,
|
|
8
|
+
type Identifier,
|
|
9
|
+
type IfStatement,
|
|
10
|
+
type ReturnStatement,
|
|
11
|
+
type SwitchStatement,
|
|
12
|
+
type TryStatement,
|
|
13
|
+
} from "../../../ast/nodes.js";
|
|
14
|
+
import { i64, XT_UNDEFINED } from "../../values.js";
|
|
15
|
+
import type { Generator } from "../generator.js";
|
|
16
|
+
|
|
17
|
+
export interface ControlFlowStatementMethods {
|
|
18
|
+
emitIf(this: Generator, statement: IfStatement): void;
|
|
19
|
+
emitSwitch(this: Generator, statement: SwitchStatement): void;
|
|
20
|
+
emitTry(this: Generator, statement: TryStatement): void;
|
|
21
|
+
bindCatchVariable(this: Generator, variable: Identifier | undefined, value: string): void;
|
|
22
|
+
emitReturn(this: Generator, statement: ReturnStatement): void;
|
|
23
|
+
popTryFramesTo(this: Generator, depth: number): void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const controlFlowStatementMethods: ControlFlowStatementMethods = {
|
|
27
|
+
emitIf(statement: IfStatement): void {
|
|
28
|
+
const thenLabel = this.label("if.then");
|
|
29
|
+
const elseLabel = statement.elseStatement ? this.label("if.else") : undefined;
|
|
30
|
+
const endLabel = this.label("if.end");
|
|
31
|
+
const condition = this.emitExpression(statement.condition);
|
|
32
|
+
const truthy = this.reg();
|
|
33
|
+
this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${condition})`);
|
|
34
|
+
const nonzero = this.reg();
|
|
35
|
+
this.emit(` ${nonzero} = icmp ne i32 ${truthy}, 0`);
|
|
36
|
+
this.terminate(`br i1 ${nonzero}, label %${thenLabel}, label %${elseLabel ?? endLabel}`);
|
|
37
|
+
|
|
38
|
+
this.startBlock(thenLabel);
|
|
39
|
+
this.emitStatement(statement.thenStatement);
|
|
40
|
+
if (!this.current.terminated) this.terminate(`br label %${endLabel}`);
|
|
41
|
+
|
|
42
|
+
if (elseLabel) {
|
|
43
|
+
this.startBlock(elseLabel);
|
|
44
|
+
this.emitStatement(statement.elseStatement!);
|
|
45
|
+
if (!this.current.terminated) this.terminate(`br label %${endLabel}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
this.startBlock(endLabel);
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* JavaScript `switch`: test each `case` with strict equality, then run the
|
|
53
|
+
* matched clause and fall through into the following clauses until `break`.
|
|
54
|
+
*/
|
|
55
|
+
emitSwitch(statement: SwitchStatement): void {
|
|
56
|
+
const discriminant = this.emitExpression(statement.expression);
|
|
57
|
+
const endLabel = this.label("switch.end");
|
|
58
|
+
const clauses = statement.clauses;
|
|
59
|
+
const labels = clauses.map(() => this.label("switch.case"));
|
|
60
|
+
const testLabels = clauses.map((clause) =>
|
|
61
|
+
clause.kind === SyntaxKind.CaseClause ? this.label("switch.test") : undefined,
|
|
62
|
+
);
|
|
63
|
+
const defaultIndex = clauses.findIndex((clause) => clause.kind === SyntaxKind.DefaultClause);
|
|
64
|
+
const defaultLabel = defaultIndex >= 0 ? labels[defaultIndex]! : endLabel;
|
|
65
|
+
const caseIndexes = clauses
|
|
66
|
+
.map((clause, index) => ({ clause, index }))
|
|
67
|
+
.filter((entry) => entry.clause.kind === SyntaxKind.CaseClause);
|
|
68
|
+
|
|
69
|
+
this.terminate(`br label %${caseIndexes.length > 0 ? testLabels[caseIndexes[0]!.index]! : defaultLabel}`);
|
|
70
|
+
for (let test = 0; test < caseIndexes.length; test++) {
|
|
71
|
+
const entry = caseIndexes[test]!;
|
|
72
|
+
this.startBlock(testLabels[entry.index]!);
|
|
73
|
+
const caseValue = this.emitExpression((entry.clause as CaseClause).expression);
|
|
74
|
+
const equals = this.reg();
|
|
75
|
+
this.emit(` ${equals} = call i64 @xt_seq(i64 ${discriminant}, i64 ${caseValue})`);
|
|
76
|
+
const truthy = this.reg();
|
|
77
|
+
this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${equals})`);
|
|
78
|
+
const condition = this.reg();
|
|
79
|
+
this.emit(` ${condition} = icmp ne i32 ${truthy}, 0`);
|
|
80
|
+
const next = test + 1 < caseIndexes.length ? testLabels[caseIndexes[test + 1]!.index]! : defaultLabel;
|
|
81
|
+
this.terminate(`br i1 ${condition}, label %${labels[entry.index]}, label %${next}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
for (let index = 0; index < clauses.length; index++) {
|
|
85
|
+
this.startBlock(labels[index]!);
|
|
86
|
+
const enclosing = this.current.loops[this.current.loops.length - 1];
|
|
87
|
+
this.current.loops.push({ breakLabel: endLabel, continueLabel: enclosing?.continueLabel ?? endLabel, tryDepth: this.current.tryFrames.length });
|
|
88
|
+
for (const child of clauses[index]!.statements) {
|
|
89
|
+
if (this.current.terminated) break;
|
|
90
|
+
this.emitStatement(child);
|
|
91
|
+
}
|
|
92
|
+
this.current.loops.pop();
|
|
93
|
+
if (!this.current.terminated) {
|
|
94
|
+
const next = index + 1 < clauses.length ? labels[index + 1]! : endLabel;
|
|
95
|
+
this.terminate(`br label %${next}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
this.startBlock(endLabel);
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* `try`/`catch`/`finally` via a runtime setjmp frame. The runtime keeps a
|
|
103
|
+
* stack of frames; `xt_throw` longjmps into the innermost one. A `return`
|
|
104
|
+
* that exits the protected region skips `finally` (a known limitation).
|
|
105
|
+
*/
|
|
106
|
+
emitTry(statement: TryStatement): void {
|
|
107
|
+
this.current.usesTry = true;
|
|
108
|
+
const frameSlot = this.alloca();
|
|
109
|
+
const exceptionSlot = this.alloca();
|
|
110
|
+
const flagSlot = this.alloca();
|
|
111
|
+
const tryLabel = this.label("try.body");
|
|
112
|
+
const catchLabel = this.label("try.catch");
|
|
113
|
+
const exceptionLabel = this.label("try.exception");
|
|
114
|
+
const rethrowLabel = this.label("try.rethrow");
|
|
115
|
+
const finallyLabel = statement.finallyBlock ? this.label("try.finally") : undefined;
|
|
116
|
+
const endLabel = this.label("try.end");
|
|
117
|
+
const hasCatch = !!statement.catchClause;
|
|
118
|
+
|
|
119
|
+
const frame = this.reg();
|
|
120
|
+
this.emit(` ${frame} = call i8* @xt_try_enter()`);
|
|
121
|
+
this.emit(` store i8* ${frame}, i8** ${frameSlot}`);
|
|
122
|
+
this.emit(` store i64 0, i64* ${flagSlot}`);
|
|
123
|
+
// MSVC's `_setjmp` takes the caller's frame address as a second argument
|
|
124
|
+
// (`_JUMP_BUFFER.Frame`); `longjmp` uses it to run the unwind. clang lowers
|
|
125
|
+
// a C call to `_setjmp` exactly like this. The extra argument is ignored by
|
|
126
|
+
// the one-argument `_setjmp` on Linux/macOS.
|
|
127
|
+
const frameAddress = this.reg();
|
|
128
|
+
this.emit(` ${frameAddress} = call i8* @llvm.frameaddress(i32 0)`);
|
|
129
|
+
const jump = this.reg();
|
|
130
|
+
this.emit(` ${jump} = call i32 @_setjmp(i8* ${frame}, i8* ${frameAddress})`);
|
|
131
|
+
const isThrow = this.reg();
|
|
132
|
+
this.emit(` ${isThrow} = icmp ne i32 ${jump}, 0`);
|
|
133
|
+
const exceptionTarget = hasCatch ? catchLabel : exceptionLabel;
|
|
134
|
+
this.terminate(`br i1 ${isThrow}, label %${exceptionTarget}, label %${tryLabel}`);
|
|
135
|
+
|
|
136
|
+
// Normal completion of the try block.
|
|
137
|
+
this.startBlock(tryLabel);
|
|
138
|
+
this.current.tryFrames.push(frameSlot);
|
|
139
|
+
this.emitStatements(statement.tryBlock.statements);
|
|
140
|
+
this.current.tryFrames.pop();
|
|
141
|
+
if (!this.current.terminated) {
|
|
142
|
+
const currentFrame = this.reg();
|
|
143
|
+
this.emit(` ${currentFrame} = load i8*, i8** ${frameSlot}`);
|
|
144
|
+
this.emit(` call void @xt_try_leave(i8* ${currentFrame})`);
|
|
145
|
+
this.terminate(`br label %${finallyLabel ?? endLabel}`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (hasCatch) {
|
|
149
|
+
this.startBlock(catchLabel);
|
|
150
|
+
const currentFrame = this.reg();
|
|
151
|
+
this.emit(` ${currentFrame} = load i8*, i8** ${frameSlot}`);
|
|
152
|
+
const exception = this.reg();
|
|
153
|
+
this.emit(` ${exception} = call i64 @xt_try_exception(i8* ${currentFrame})`);
|
|
154
|
+
this.emit(` call void @xt_try_leave(i8* ${currentFrame})`);
|
|
155
|
+
this.bindCatchVariable(statement.catchClause!.variable, exception);
|
|
156
|
+
this.emitStatements(statement.catchClause!.block.statements);
|
|
157
|
+
if (!this.current.terminated) this.terminate(`br label %${finallyLabel ?? endLabel}`);
|
|
158
|
+
} else {
|
|
159
|
+
// Without a catch clause the exception is remembered, then rethrown
|
|
160
|
+
// after `finally` runs.
|
|
161
|
+
this.startBlock(exceptionLabel);
|
|
162
|
+
const currentFrame = this.reg();
|
|
163
|
+
this.emit(` ${currentFrame} = load i8*, i8** ${frameSlot}`);
|
|
164
|
+
const exception = this.reg();
|
|
165
|
+
this.emit(` ${exception} = call i64 @xt_try_exception(i8* ${currentFrame})`);
|
|
166
|
+
this.emit(` call void @xt_try_leave(i8* ${currentFrame})`);
|
|
167
|
+
this.emit(` store i64 ${exception}, i64* ${exceptionSlot}`);
|
|
168
|
+
this.emit(` store i64 1, i64* ${flagSlot}`);
|
|
169
|
+
this.terminate(`br label %${finallyLabel ?? rethrowLabel}`);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (finallyLabel) {
|
|
173
|
+
this.startBlock(finallyLabel);
|
|
174
|
+
this.emitStatements(statement.finallyBlock!.statements);
|
|
175
|
+
if (!this.current.terminated) {
|
|
176
|
+
if (hasCatch) {
|
|
177
|
+
this.terminate(`br label %${endLabel}`);
|
|
178
|
+
} else {
|
|
179
|
+
const flag = this.reg();
|
|
180
|
+
this.emit(` ${flag} = load i64, i64* ${flagSlot}`);
|
|
181
|
+
const truthy = this.reg();
|
|
182
|
+
this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${flag})`);
|
|
183
|
+
const shouldRethrow = this.reg();
|
|
184
|
+
this.emit(` ${shouldRethrow} = icmp ne i32 ${truthy}, 0`);
|
|
185
|
+
this.terminate(`br i1 ${shouldRethrow}, label %${rethrowLabel}, label %${endLabel}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (!hasCatch) {
|
|
191
|
+
this.startBlock(rethrowLabel);
|
|
192
|
+
const exception = this.reg();
|
|
193
|
+
this.emit(` ${exception} = load i64, i64* ${exceptionSlot}`);
|
|
194
|
+
this.emit(` call void @xt_throw(i64 ${exception})`);
|
|
195
|
+
this.terminate("unreachable");
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
this.startBlock(endLabel);
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
bindCatchVariable(variable: Identifier | undefined, value: string): void {
|
|
202
|
+
if (!variable) return;
|
|
203
|
+
const symbol = this.binding.symbolOfDeclaration.get(variable);
|
|
204
|
+
if (!symbol) return;
|
|
205
|
+
if (this.current.slots.has(symbol.id)) this.writeSlot(symbol, value);
|
|
206
|
+
else this.declareSlot(symbol, value);
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
emitReturn(statement: ReturnStatement): void {
|
|
210
|
+
let value = statement.expression ? this.emitExpression(statement.expression) : i64(XT_UNDEFINED);
|
|
211
|
+
if (this.current.fn.isAsync) value = this.wrapAsync(value);
|
|
212
|
+
this.popTryFramesTo(0);
|
|
213
|
+
this.terminate(`ret i64 ${value}`);
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
/** Emit `xt_try_leave` for every active frame above `depth` (innermost first). */
|
|
217
|
+
popTryFramesTo(depth: number): void {
|
|
218
|
+
for (let index = this.current.tryFrames.length - 1; index >= depth; index--) {
|
|
219
|
+
const slot = this.current.tryFrames[index]!;
|
|
220
|
+
const frame = this.reg();
|
|
221
|
+
this.emit(` ${frame} = load i8*, i8** ${slot}`);
|
|
222
|
+
this.emit(` call void @xt_try_leave(i8* ${frame})`);
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Statement dispatch: walks a statement list and routes each kind to its
|
|
3
|
+
* lowering method. The concrete lowerings live in the sibling modules
|
|
4
|
+
* (`variables`, `control-flow`, `loops`).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
SyntaxKind,
|
|
9
|
+
type Block,
|
|
10
|
+
type DoStatement,
|
|
11
|
+
type EnumDeclaration,
|
|
12
|
+
type Expression,
|
|
13
|
+
type ForInStatement,
|
|
14
|
+
type ForOfStatement,
|
|
15
|
+
type ForStatement,
|
|
16
|
+
type IfStatement,
|
|
17
|
+
type ReturnStatement,
|
|
18
|
+
type Statement,
|
|
19
|
+
type SwitchStatement,
|
|
20
|
+
type ThrowStatement,
|
|
21
|
+
type TryStatement,
|
|
22
|
+
type VariableStatement,
|
|
23
|
+
type WhileStatement,
|
|
24
|
+
} from "../../../ast/nodes.js";
|
|
25
|
+
import type { Generator } from "../generator.js";
|
|
26
|
+
|
|
27
|
+
export interface StatementDispatchMethods {
|
|
28
|
+
emitStatements(this: Generator, statements: readonly Statement[]): void;
|
|
29
|
+
emitStatement(this: Generator, statement: Statement): void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const statementDispatchMethods: StatementDispatchMethods = {
|
|
33
|
+
emitStatements(statements: readonly Statement[]): void {
|
|
34
|
+
for (const statement of statements) {
|
|
35
|
+
if (this.current.terminated) break;
|
|
36
|
+
this.emitStatement(statement);
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
emitStatement(statement: Statement): void {
|
|
41
|
+
switch (statement.kind) {
|
|
42
|
+
case SyntaxKind.ExpressionStatement:
|
|
43
|
+
this.emitExpression((statement as { expression: Expression }).expression);
|
|
44
|
+
return;
|
|
45
|
+
case SyntaxKind.VariableStatement:
|
|
46
|
+
this.emitVariableStatement(statement as VariableStatement);
|
|
47
|
+
return;
|
|
48
|
+
case SyntaxKind.Block:
|
|
49
|
+
this.emitStatements((statement as Block).statements);
|
|
50
|
+
return;
|
|
51
|
+
case SyntaxKind.EmptyStatement:
|
|
52
|
+
case SyntaxKind.DebuggerStatement:
|
|
53
|
+
case SyntaxKind.FunctionDeclaration:
|
|
54
|
+
case SyntaxKind.InterfaceDeclaration:
|
|
55
|
+
case SyntaxKind.TypeAliasDeclaration:
|
|
56
|
+
case SyntaxKind.ImportDeclaration:
|
|
57
|
+
return; // functions are emitted at module scope; imports are resolved at call sites
|
|
58
|
+
case SyntaxKind.ClassDeclaration: {
|
|
59
|
+
const info = this.binding.classOfNode.get(statement);
|
|
60
|
+
if (info) this.emitClassSetup(info);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
case SyntaxKind.IfStatement:
|
|
64
|
+
this.emitIf(statement as IfStatement);
|
|
65
|
+
return;
|
|
66
|
+
case SyntaxKind.WhileStatement:
|
|
67
|
+
this.emitWhile(statement as WhileStatement);
|
|
68
|
+
return;
|
|
69
|
+
case SyntaxKind.DoStatement:
|
|
70
|
+
this.emitDo(statement as DoStatement);
|
|
71
|
+
return;
|
|
72
|
+
case SyntaxKind.ForStatement:
|
|
73
|
+
this.emitFor(statement as ForStatement);
|
|
74
|
+
return;
|
|
75
|
+
case SyntaxKind.ForOfStatement:
|
|
76
|
+
case SyntaxKind.ForInStatement:
|
|
77
|
+
this.emitForOf(statement as ForOfStatement | ForInStatement);
|
|
78
|
+
return;
|
|
79
|
+
case SyntaxKind.SwitchStatement:
|
|
80
|
+
this.emitSwitch(statement as SwitchStatement);
|
|
81
|
+
return;
|
|
82
|
+
case SyntaxKind.TryStatement:
|
|
83
|
+
this.emitTry(statement as TryStatement);
|
|
84
|
+
return;
|
|
85
|
+
case SyntaxKind.ReturnStatement:
|
|
86
|
+
this.emitReturn(statement as ReturnStatement);
|
|
87
|
+
return;
|
|
88
|
+
case SyntaxKind.BreakStatement: {
|
|
89
|
+
const loop = this.current.loops[this.current.loops.length - 1];
|
|
90
|
+
if (loop) {
|
|
91
|
+
this.popTryFramesTo(loop.tryDepth ?? 0);
|
|
92
|
+
this.terminate(`br label %${loop.breakLabel}`);
|
|
93
|
+
}
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
case SyntaxKind.ContinueStatement: {
|
|
97
|
+
const loop = this.current.loops[this.current.loops.length - 1];
|
|
98
|
+
if (loop) {
|
|
99
|
+
this.popTryFramesTo(loop.tryDepth ?? 0);
|
|
100
|
+
this.terminate(`br label %${loop.continueLabel}`);
|
|
101
|
+
}
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
case SyntaxKind.ThrowStatement: {
|
|
105
|
+
const value = this.emitExpression((statement as ThrowStatement).expression);
|
|
106
|
+
this.emit(` call void @xt_throw(i64 ${value})`);
|
|
107
|
+
this.terminate("unreachable");
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
case SyntaxKind.ExportDeclaration: {
|
|
111
|
+
const declaration = (statement as { declaration?: Statement }).declaration;
|
|
112
|
+
if (declaration) this.emitStatement(declaration);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
case SyntaxKind.EnumDeclaration:
|
|
116
|
+
this.emitEnum(statement as EnumDeclaration);
|
|
117
|
+
return;
|
|
118
|
+
default:
|
|
119
|
+
this.unsupported(statement, "statement");
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
};
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loop lowering: `while`, `do`/`while`, `for` and `for`/`of` / `for`/`in`.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
SyntaxKind,
|
|
7
|
+
type DoStatement,
|
|
8
|
+
type Expression,
|
|
9
|
+
type ForInStatement,
|
|
10
|
+
type ForOfStatement,
|
|
11
|
+
type ForStatement,
|
|
12
|
+
type VariableDeclarationList,
|
|
13
|
+
type WhileStatement,
|
|
14
|
+
} from "../../../ast/nodes.js";
|
|
15
|
+
import { numberLiteral } from "../../values.js";
|
|
16
|
+
import type { Generator } from "../generator.js";
|
|
17
|
+
|
|
18
|
+
export interface LoopStatementMethods {
|
|
19
|
+
emitWhile(this: Generator, statement: WhileStatement): void;
|
|
20
|
+
emitDo(this: Generator, statement: DoStatement): void;
|
|
21
|
+
emitFor(this: Generator, statement: ForStatement): void;
|
|
22
|
+
emitForOf(this: Generator, statement: ForOfStatement | ForInStatement): void;
|
|
23
|
+
bindLoopVariable(this: Generator, initializer: VariableDeclarationList | Expression, value: string): void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const loopStatementMethods: LoopStatementMethods = {
|
|
27
|
+
emitWhile(statement: WhileStatement): void {
|
|
28
|
+
const condLabel = this.label("while.cond");
|
|
29
|
+
const bodyLabel = this.label("while.body");
|
|
30
|
+
const endLabel = this.label("while.end");
|
|
31
|
+
this.terminate(`br label %${condLabel}`);
|
|
32
|
+
this.startBlock(condLabel);
|
|
33
|
+
const condition = this.emitExpression(statement.condition);
|
|
34
|
+
const truthy = this.reg();
|
|
35
|
+
this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${condition})`);
|
|
36
|
+
const nonzero = this.reg();
|
|
37
|
+
this.emit(` ${nonzero} = icmp ne i32 ${truthy}, 0`);
|
|
38
|
+
this.terminate(`br i1 ${nonzero}, label %${bodyLabel}, label %${endLabel}`);
|
|
39
|
+
this.startBlock(bodyLabel);
|
|
40
|
+
this.current.loops.push({ breakLabel: endLabel, continueLabel: condLabel, tryDepth: this.current.tryFrames.length });
|
|
41
|
+
this.emitStatement(statement.statement);
|
|
42
|
+
this.current.loops.pop();
|
|
43
|
+
if (!this.current.terminated) this.terminate(`br label %${condLabel}`);
|
|
44
|
+
this.startBlock(endLabel);
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
emitDo(statement: DoStatement): void {
|
|
48
|
+
const bodyLabel = this.label("do.body");
|
|
49
|
+
const condLabel = this.label("do.cond");
|
|
50
|
+
const endLabel = this.label("do.end");
|
|
51
|
+
this.terminate(`br label %${bodyLabel}`);
|
|
52
|
+
this.startBlock(bodyLabel);
|
|
53
|
+
this.current.loops.push({ breakLabel: endLabel, continueLabel: condLabel, tryDepth: this.current.tryFrames.length });
|
|
54
|
+
this.emitStatement(statement.statement);
|
|
55
|
+
this.current.loops.pop();
|
|
56
|
+
if (!this.current.terminated) this.terminate(`br label %${condLabel}`);
|
|
57
|
+
|
|
58
|
+
this.startBlock(condLabel);
|
|
59
|
+
const condition = this.emitExpression(statement.condition);
|
|
60
|
+
const truthy = this.reg();
|
|
61
|
+
this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${condition})`);
|
|
62
|
+
const nonzero = this.reg();
|
|
63
|
+
this.emit(` ${nonzero} = icmp ne i32 ${truthy}, 0`);
|
|
64
|
+
this.terminate(`br i1 ${nonzero}, label %${bodyLabel}, label %${endLabel}`);
|
|
65
|
+
this.startBlock(endLabel);
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
emitFor(statement: ForStatement): void {
|
|
69
|
+
if (statement.initializer && statement.initializer.kind === SyntaxKind.VariableDeclarationList) {
|
|
70
|
+
this.emitVariableStatement({
|
|
71
|
+
kind: SyntaxKind.VariableStatement,
|
|
72
|
+
declarationList: statement.initializer as VariableDeclarationList,
|
|
73
|
+
modifiers: [],
|
|
74
|
+
start: statement.initializer.start,
|
|
75
|
+
end: statement.initializer.end,
|
|
76
|
+
});
|
|
77
|
+
} else if (statement.initializer) {
|
|
78
|
+
this.emitExpression(statement.initializer as Expression);
|
|
79
|
+
}
|
|
80
|
+
const condLabel = this.label("for.cond");
|
|
81
|
+
const bodyLabel = this.label("for.body");
|
|
82
|
+
const updateLabel = this.label("for.update");
|
|
83
|
+
const endLabel = this.label("for.end");
|
|
84
|
+
this.terminate(`br label %${condLabel}`);
|
|
85
|
+
|
|
86
|
+
this.startBlock(condLabel);
|
|
87
|
+
if (statement.condition) {
|
|
88
|
+
const condition = this.emitExpression(statement.condition);
|
|
89
|
+
const truthy = this.reg();
|
|
90
|
+
this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${condition})`);
|
|
91
|
+
const nonzero = this.reg();
|
|
92
|
+
this.emit(` ${nonzero} = icmp ne i32 ${truthy}, 0`);
|
|
93
|
+
this.terminate(`br i1 ${nonzero}, label %${bodyLabel}, label %${endLabel}`);
|
|
94
|
+
} else {
|
|
95
|
+
this.terminate(`br label %${bodyLabel}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
this.startBlock(bodyLabel);
|
|
99
|
+
this.current.loops.push({ breakLabel: endLabel, continueLabel: updateLabel, tryDepth: this.current.tryFrames.length });
|
|
100
|
+
this.emitStatement(statement.statement);
|
|
101
|
+
this.current.loops.pop();
|
|
102
|
+
if (!this.current.terminated) this.terminate(`br label %${updateLabel}`);
|
|
103
|
+
|
|
104
|
+
this.startBlock(updateLabel);
|
|
105
|
+
if (statement.incrementor) this.emitExpression(statement.incrementor);
|
|
106
|
+
this.terminate(`br label %${condLabel}`);
|
|
107
|
+
this.startBlock(endLabel);
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
/** `for (const x of xs)` / `for (const k in obj)` over arrays, strings and objects. */
|
|
111
|
+
emitForOf(statement: ForOfStatement | ForInStatement): void {
|
|
112
|
+
const source = this.emitExpression(statement.expression);
|
|
113
|
+
const isForIn = statement.kind === SyntaxKind.ForInStatement;
|
|
114
|
+
// `for...in` iterates the enumerable keys (indices become strings);
|
|
115
|
+
// `for...of` iterates the values at each index.
|
|
116
|
+
const iterable = isForIn ? this.runtimeCall("xt_object_keys", [source]) : source;
|
|
117
|
+
const indexPtr = this.alloca();
|
|
118
|
+
this.emit(` store i64 ${numberLiteral(0)}, i64* ${indexPtr}`);
|
|
119
|
+
const lengthValue = this.reg();
|
|
120
|
+
this.emit(` ${lengthValue} = call i64 @xt_iter_length(i64 ${iterable})`);
|
|
121
|
+
|
|
122
|
+
const condLabel = this.label("forof.cond");
|
|
123
|
+
const bodyLabel = this.label("forof.body");
|
|
124
|
+
const updateLabel = this.label("forof.update");
|
|
125
|
+
const endLabel = this.label("forof.end");
|
|
126
|
+
this.terminate(`br label %${condLabel}`);
|
|
127
|
+
|
|
128
|
+
this.startBlock(condLabel);
|
|
129
|
+
const index = this.reg();
|
|
130
|
+
this.emit(` ${index} = load i64, i64* ${indexPtr}`);
|
|
131
|
+
const inRange = this.reg();
|
|
132
|
+
this.emit(` ${inRange} = call i64 @xt_lt(i64 ${index}, i64 ${lengthValue})`);
|
|
133
|
+
const truthy = this.reg();
|
|
134
|
+
this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${inRange})`);
|
|
135
|
+
const nonzero = this.reg();
|
|
136
|
+
this.emit(` ${nonzero} = icmp ne i32 ${truthy}, 0`);
|
|
137
|
+
this.terminate(`br i1 ${nonzero}, label %${bodyLabel}, label %${endLabel}`);
|
|
138
|
+
|
|
139
|
+
this.startBlock(bodyLabel);
|
|
140
|
+
const element = this.reg();
|
|
141
|
+
this.emit(` ${element} = call i64 @xt_iter_value(i64 ${iterable}, i64 ${index})`);
|
|
142
|
+
this.bindLoopVariable(statement.initializer, element);
|
|
143
|
+
this.current.loops.push({ breakLabel: endLabel, continueLabel: updateLabel, tryDepth: this.current.tryFrames.length });
|
|
144
|
+
this.emitStatement(statement.statement);
|
|
145
|
+
this.current.loops.pop();
|
|
146
|
+
if (!this.current.terminated) this.terminate(`br label %${updateLabel}`);
|
|
147
|
+
|
|
148
|
+
this.startBlock(updateLabel);
|
|
149
|
+
const next = this.reg();
|
|
150
|
+
this.emit(` ${next} = call i64 @xt_add(i64 ${index}, i64 ${numberLiteral(1)})`);
|
|
151
|
+
this.emit(` store i64 ${next}, i64* ${indexPtr}`);
|
|
152
|
+
this.terminate(`br label %${condLabel}`);
|
|
153
|
+
this.startBlock(endLabel);
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
bindLoopVariable(initializer: VariableDeclarationList | Expression, value: string): void {
|
|
157
|
+
if (initializer.kind === SyntaxKind.VariableDeclarationList) {
|
|
158
|
+
const declaration = initializer.declarations[0];
|
|
159
|
+
if (!declaration) return;
|
|
160
|
+
if (declaration.name.kind !== SyntaxKind.Identifier) {
|
|
161
|
+
this.emitBindingPattern(declaration.name, value);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const symbol = this.binding.symbolOfDeclaration.get(declaration) ?? this.binding.symbolOfDeclaration.get(declaration.name);
|
|
165
|
+
if (symbol) {
|
|
166
|
+
if (this.current.slots.has(symbol.id)) this.writeSlot(symbol, value);
|
|
167
|
+
else this.declareSlot(symbol, value);
|
|
168
|
+
}
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
this.emitAssignmentTarget(initializer as Expression, value);
|
|
172
|
+
},
|
|
173
|
+
};
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Variable lowering: declarations, destructuring bindings, default values and
|
|
3
|
+
* enum objects.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
SyntaxKind,
|
|
8
|
+
type ArrayBindingPattern,
|
|
9
|
+
type BindingName,
|
|
10
|
+
type EnumDeclaration,
|
|
11
|
+
type Expression,
|
|
12
|
+
type ObjectBindingPattern,
|
|
13
|
+
type VariableStatement,
|
|
14
|
+
} from "../../../ast/nodes.js";
|
|
15
|
+
import { i64, numberLiteral, XT_UNDEFINED } from "../../values.js";
|
|
16
|
+
import { propertyNameText } from "../tables.js";
|
|
17
|
+
import type { Generator } from "../generator.js";
|
|
18
|
+
|
|
19
|
+
export interface VariableStatementMethods {
|
|
20
|
+
emitVariableStatement(this: Generator, statement: VariableStatement): void;
|
|
21
|
+
emitBindingPattern(this: Generator, name: BindingName, value: string): void;
|
|
22
|
+
emitBindingDefault(this: Generator, value: string, initializer: Expression): string;
|
|
23
|
+
emitEnum(this: Generator, statement: EnumDeclaration): void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const variableStatementMethods: VariableStatementMethods = {
|
|
27
|
+
emitVariableStatement(statement: VariableStatement): void {
|
|
28
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
29
|
+
if (declaration.name.kind === SyntaxKind.Identifier) {
|
|
30
|
+
const symbol = this.binding.symbolOfDeclaration.get(declaration);
|
|
31
|
+
if (!symbol) {
|
|
32
|
+
if (declaration.initializer) this.emitExpression(declaration.initializer);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (this.current.slots.has(symbol.id)) {
|
|
36
|
+
// Hoisted `var` (or an already-declared slot): keep evaluating the
|
|
37
|
+
// initializer for its side effects, but do not redeclare the slot.
|
|
38
|
+
if (declaration.initializer) this.emitExpression(declaration.initializer);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (symbol.boxed) {
|
|
42
|
+
// A captured binding lives in a box. Create the box *before*
|
|
43
|
+
// evaluating the initializer so a self-referencing closure
|
|
44
|
+
// (`const f = () => f()`) captures the box, not a copy of the
|
|
45
|
+
// still-uninitialised value.
|
|
46
|
+
this.declareSlot(symbol, i64(XT_UNDEFINED));
|
|
47
|
+
if (declaration.initializer) this.writeSlot(symbol, this.emitExpression(declaration.initializer));
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const initial = declaration.initializer ? this.emitExpression(declaration.initializer) : i64(XT_UNDEFINED);
|
|
51
|
+
this.declareSlot(symbol, initial);
|
|
52
|
+
} else {
|
|
53
|
+
const initial = declaration.initializer ? this.emitExpression(declaration.initializer) : i64(XT_UNDEFINED);
|
|
54
|
+
this.emitBindingPattern(declaration.name, initial);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
/** Lower a destructuring binding pattern, declaring each bound identifier. */
|
|
60
|
+
emitBindingPattern(name: BindingName, value: string): void {
|
|
61
|
+
if (name.kind === SyntaxKind.Identifier) {
|
|
62
|
+
const symbol = this.binding.symbolOfDeclaration.get(name);
|
|
63
|
+
if (!symbol) return;
|
|
64
|
+
if (this.current.slots.has(symbol.id)) this.writeSlot(symbol, value);
|
|
65
|
+
else this.declareSlot(symbol, value);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (name.kind === SyntaxKind.ArrayBindingPattern) {
|
|
69
|
+
const pattern = name as ArrayBindingPattern;
|
|
70
|
+
for (let index = 0; index < pattern.elements.length; index++) {
|
|
71
|
+
const element = pattern.elements[index];
|
|
72
|
+
if (!element) continue;
|
|
73
|
+
let elementValue: string;
|
|
74
|
+
if (element.dotDotDotToken) {
|
|
75
|
+
const startPtr = this.alloca();
|
|
76
|
+
this.emit(` store i64 ${numberLiteral(index)}, i64* ${startPtr}`);
|
|
77
|
+
const sliceName = this.stringValue("slice");
|
|
78
|
+
elementValue = this.reg();
|
|
79
|
+
this.emit(` ${elementValue} = call i64 @xt_call_method(i64 ${value}, i64 ${sliceName}, i32 1, i64* ${startPtr})`);
|
|
80
|
+
} else {
|
|
81
|
+
elementValue = this.reg();
|
|
82
|
+
this.emit(` ${elementValue} = call i64 @xt_get(i64 ${value}, i64 ${numberLiteral(index)})`);
|
|
83
|
+
}
|
|
84
|
+
if (element.initializer) elementValue = this.emitBindingDefault(elementValue, element.initializer);
|
|
85
|
+
this.emitBindingPattern(element.name, elementValue);
|
|
86
|
+
}
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const pattern = name as ObjectBindingPattern;
|
|
90
|
+
for (const element of pattern.elements) {
|
|
91
|
+
const key = element.propertyName ?? (element.name.kind === SyntaxKind.Identifier ? element.name : undefined);
|
|
92
|
+
if (element.dotDotDotToken) {
|
|
93
|
+
this.unsupported(element, "object rest destructuring");
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const keyValue = this.stringValue(key ? propertyNameText(key) : "undefined");
|
|
97
|
+
let elementValue = this.reg();
|
|
98
|
+
this.emit(` ${elementValue} = call i64 @xt_get(i64 ${value}, i64 ${keyValue})`);
|
|
99
|
+
if (element.initializer) elementValue = this.emitBindingDefault(elementValue, element.initializer);
|
|
100
|
+
this.emitBindingPattern(element.name, elementValue);
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
/** `x = fallback` in a binding pattern: only apply when `x` is `undefined`. */
|
|
105
|
+
emitBindingDefault(value: string, initializer: Expression): string {
|
|
106
|
+
const fallback = this.emitExpression(initializer);
|
|
107
|
+
const isUndefined = this.reg();
|
|
108
|
+
this.emit(` ${isUndefined} = call i64 @xt_seq(i64 ${value}, i64 ${i64(XT_UNDEFINED)})`);
|
|
109
|
+
const truthy = this.reg();
|
|
110
|
+
this.emit(` ${truthy} = call i32 @xt_truthy(i64 ${isUndefined})`);
|
|
111
|
+
const condition = this.reg();
|
|
112
|
+
this.emit(` ${condition} = icmp ne i32 ${truthy}, 0`);
|
|
113
|
+
const result = this.reg();
|
|
114
|
+
this.emit(` ${result} = select i1 ${condition}, i64 ${fallback}, i64 ${value}`);
|
|
115
|
+
return result;
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* `enum E { A, B }` (and `const enum`) lowers to a runtime object with both
|
|
120
|
+
* forward (`E.A`) and reverse (`E[0] === "A"`) numeric mappings.
|
|
121
|
+
*/
|
|
122
|
+
emitEnum(statement: EnumDeclaration): void {
|
|
123
|
+
const symbol = this.binding.symbolOfDeclaration.get(statement);
|
|
124
|
+
const object = this.reg();
|
|
125
|
+
this.emit(` ${object} = call i64 @xt_object_new()`);
|
|
126
|
+
let next = 0;
|
|
127
|
+
for (const member of statement.members) {
|
|
128
|
+
const name = propertyNameText(member.name);
|
|
129
|
+
const key = this.stringValue(name);
|
|
130
|
+
let value: string;
|
|
131
|
+
if (member.initializer) {
|
|
132
|
+
value = this.emitExpression(member.initializer);
|
|
133
|
+
if (member.initializer.kind === SyntaxKind.NumericLiteral) {
|
|
134
|
+
const numeric = (member.initializer as { value?: number }).value ?? 0;
|
|
135
|
+
next = numeric + 1;
|
|
136
|
+
} else {
|
|
137
|
+
next++;
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
value = numberLiteral(next);
|
|
141
|
+
next++;
|
|
142
|
+
}
|
|
143
|
+
this.emit(` call i64 @xt_set(i64 ${object}, i64 ${key}, i64 ${value})`);
|
|
144
|
+
// Reverse mapping (only meaningful for numeric members).
|
|
145
|
+
if (!member.initializer || member.initializer.kind === SyntaxKind.NumericLiteral) {
|
|
146
|
+
const reverseKey = this.stringValue(name);
|
|
147
|
+
this.emit(` call i64 @xt_set(i64 ${object}, i64 ${value}, i64 ${reverseKey})`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (symbol) this.declareSlot(symbol, object);
|
|
151
|
+
},
|
|
152
|
+
};
|