xbintsc 0.1.0
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/LICENSE +21 -0
- package/README.md +191 -0
- package/bin/xbintsc.js +26 -0
- package/dist/src/ast/factory.d.ts +11 -0
- package/dist/src/ast/factory.js +36 -0
- package/dist/src/ast/factory.js.map +1 -0
- package/dist/src/ast/nodes.d.ts +848 -0
- package/dist/src/ast/nodes.js +247 -0
- package/dist/src/ast/nodes.js.map +1 -0
- package/dist/src/ast/visitor.d.ts +18 -0
- package/dist/src/ast/visitor.js +52 -0
- package/dist/src/ast/visitor.js.map +1 -0
- package/dist/src/binder/binder.d.ts +82 -0
- package/dist/src/binder/binder.js +443 -0
- package/dist/src/binder/binder.js.map +1 -0
- package/dist/src/cli/main.d.ts +16 -0
- package/dist/src/cli/main.js +191 -0
- package/dist/src/cli/main.js.map +1 -0
- package/dist/src/codegen/llvm.d.ts +31 -0
- package/dist/src/codegen/llvm.js +1178 -0
- package/dist/src/codegen/llvm.js.map +1 -0
- package/dist/src/codegen/values.d.ts +23 -0
- package/dist/src/codegen/values.js +32 -0
- package/dist/src/codegen/values.js.map +1 -0
- package/dist/src/diagnostics/diagnostic.d.ts +69 -0
- package/dist/src/diagnostics/diagnostic.js +104 -0
- package/dist/src/diagnostics/diagnostic.js.map +1 -0
- package/dist/src/diagnostics/source.d.ts +43 -0
- package/dist/src/diagnostics/source.js +73 -0
- package/dist/src/diagnostics/source.js.map +1 -0
- package/dist/src/driver/cache.d.ts +37 -0
- package/dist/src/driver/cache.js +82 -0
- package/dist/src/driver/cache.js.map +1 -0
- package/dist/src/driver/compiler.d.ts +41 -0
- package/dist/src/driver/compiler.js +128 -0
- package/dist/src/driver/compiler.js.map +1 -0
- package/dist/src/driver/index.d.ts +4 -0
- package/dist/src/driver/index.js +5 -0
- package/dist/src/driver/index.js.map +1 -0
- package/dist/src/driver/paths.d.ts +4 -0
- package/dist/src/driver/paths.js +22 -0
- package/dist/src/driver/paths.js.map +1 -0
- package/dist/src/driver/toolchain.d.ts +43 -0
- package/dist/src/driver/toolchain.js +76 -0
- package/dist/src/driver/toolchain.js.map +1 -0
- package/dist/src/extensions/node.d.ts +9 -0
- package/dist/src/extensions/node.js +20 -0
- package/dist/src/extensions/node.js.map +1 -0
- package/dist/src/extensions/registry.d.ts +45 -0
- package/dist/src/extensions/registry.js +76 -0
- package/dist/src/extensions/registry.js.map +1 -0
- package/dist/src/index.d.ts +18 -0
- package/dist/src/index.js +19 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/lexer/scanner.d.ts +48 -0
- package/dist/src/lexer/scanner.js +650 -0
- package/dist/src/lexer/scanner.js.map +1 -0
- package/dist/src/lexer/token.d.ts +170 -0
- package/dist/src/lexer/token.js +250 -0
- package/dist/src/lexer/token.js.map +1 -0
- package/dist/src/parser/parser.d.ts +127 -0
- package/dist/src/parser/parser.js +2334 -0
- package/dist/src/parser/parser.js.map +1 -0
- package/dist/tests/binder/binder.test.d.ts +1 -0
- package/dist/tests/binder/binder.test.js +89 -0
- package/dist/tests/binder/binder.test.js.map +1 -0
- package/dist/tests/cli/main.test.d.ts +1 -0
- package/dist/tests/cli/main.test.js +68 -0
- package/dist/tests/cli/main.test.js.map +1 -0
- package/dist/tests/cli/process.test.d.ts +9 -0
- package/dist/tests/cli/process.test.js +57 -0
- package/dist/tests/cli/process.test.js.map +1 -0
- package/dist/tests/codegen/llvm.test.d.ts +1 -0
- package/dist/tests/codegen/llvm.test.js +93 -0
- package/dist/tests/codegen/llvm.test.js.map +1 -0
- package/dist/tests/driver/cache.test.d.ts +1 -0
- package/dist/tests/driver/cache.test.js +66 -0
- package/dist/tests/driver/cache.test.js.map +1 -0
- package/dist/tests/driver/compiler.test.d.ts +1 -0
- package/dist/tests/driver/compiler.test.js +80 -0
- package/dist/tests/driver/compiler.test.js.map +1 -0
- package/dist/tests/driver/toolchain.test.d.ts +1 -0
- package/dist/tests/driver/toolchain.test.js +60 -0
- package/dist/tests/driver/toolchain.test.js.map +1 -0
- package/dist/tests/e2e/compile.test.d.ts +5 -0
- package/dist/tests/e2e/compile.test.js +93 -0
- package/dist/tests/e2e/compile.test.js.map +1 -0
- package/dist/tests/extensions/registry.test.d.ts +1 -0
- package/dist/tests/extensions/registry.test.js +47 -0
- package/dist/tests/extensions/registry.test.js.map +1 -0
- package/dist/tests/helpers.d.ts +23 -0
- package/dist/tests/helpers.js +41 -0
- package/dist/tests/helpers.js.map +1 -0
- package/dist/tests/lexer/scanner.test.d.ts +1 -0
- package/dist/tests/lexer/scanner.test.js +95 -0
- package/dist/tests/lexer/scanner.test.js.map +1 -0
- package/dist/tests/parser/parser.test.d.ts +1 -0
- package/dist/tests/parser/parser.test.js +137 -0
- package/dist/tests/parser/parser.test.js.map +1 -0
- package/package.json +49 -0
- package/runtime/ext_node.c +42 -0
- package/runtime/rt.h +177 -0
- package/runtime/xt_runtime.c +682 -0
- package/scripts/build-runtime.mjs +34 -0
- package/src/ast/factory.ts +37 -0
- package/src/ast/nodes.ts +1111 -0
- package/src/ast/visitor.ts +60 -0
- package/src/binder/binder.ts +549 -0
- package/src/cli/main.ts +210 -0
- package/src/codegen/llvm.ts +1324 -0
- package/src/codegen/values.ts +36 -0
- package/src/diagnostics/diagnostic.ts +132 -0
- package/src/diagnostics/source.ts +97 -0
- package/src/driver/cache.ts +96 -0
- package/src/driver/compiler.ts +197 -0
- package/src/driver/index.ts +4 -0
- package/src/driver/paths.ts +24 -0
- package/src/driver/toolchain.ts +103 -0
- package/src/extensions/node.ts +23 -0
- package/src/extensions/registry.ts +101 -0
- package/src/index.ts +33 -0
- package/src/lexer/scanner.ts +650 -0
- package/src/lexer/token.ts +266 -0
- package/src/parser/parser.ts +2396 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
5
|
+
import { BuildCache, hashParts, hashString } from "../../src/driver/cache.js";
|
|
6
|
+
const directories = [];
|
|
7
|
+
function temporaryDirectory() {
|
|
8
|
+
const directory = mkdtempSync(join(tmpdir(), "xbintsc-cache-"));
|
|
9
|
+
directories.push(directory);
|
|
10
|
+
return directory;
|
|
11
|
+
}
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
while (directories.length > 0)
|
|
14
|
+
rmSync(directories.pop(), { recursive: true, force: true });
|
|
15
|
+
});
|
|
16
|
+
describe("hashing", () => {
|
|
17
|
+
it("is deterministic for the same content", () => {
|
|
18
|
+
expect(hashString("hello")).toBe(hashString("hello"));
|
|
19
|
+
expect(hashString("hello")).not.toBe(hashString("hellO"));
|
|
20
|
+
});
|
|
21
|
+
it("is injective across part boundaries", () => {
|
|
22
|
+
// "ab"+"c" must not collide with "a"+"bc".
|
|
23
|
+
expect(hashParts(["ab", "c"])).not.toBe(hashParts(["a", "bc"]));
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
describe("BuildCache", () => {
|
|
27
|
+
it("reports missing entries as not fresh", () => {
|
|
28
|
+
const cache = new BuildCache(temporaryDirectory());
|
|
29
|
+
expect(cache.isFresh("key", ["/does/not/exist"])).toBe(false);
|
|
30
|
+
});
|
|
31
|
+
it("treats an entry as fresh only when its outputs exist", () => {
|
|
32
|
+
const directory = temporaryDirectory();
|
|
33
|
+
const output = join(directory, "out.bin");
|
|
34
|
+
const cache = new BuildCache(directory);
|
|
35
|
+
cache.record("key", [output]);
|
|
36
|
+
expect(cache.isFresh("key", [output])).toBe(false);
|
|
37
|
+
writeFileSync(output, "x");
|
|
38
|
+
expect(cache.isFresh("key", [output])).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
it("persists entries across instances", () => {
|
|
41
|
+
const directory = temporaryDirectory();
|
|
42
|
+
const output = join(directory, "artifact");
|
|
43
|
+
const first = new BuildCache(directory);
|
|
44
|
+
first.record("key", [output]);
|
|
45
|
+
first.save();
|
|
46
|
+
expect(first.isFresh("key", [output])).toBe(false); // output missing
|
|
47
|
+
writeFileSync(output, "x");
|
|
48
|
+
const second = new BuildCache(directory);
|
|
49
|
+
expect(second.isFresh("key", [output])).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
it("clears entries", () => {
|
|
52
|
+
const directory = temporaryDirectory();
|
|
53
|
+
const cache = new BuildCache(directory);
|
|
54
|
+
cache.record("key", ["x"]);
|
|
55
|
+
expect(cache.entries()).toHaveLength(1);
|
|
56
|
+
cache.clear();
|
|
57
|
+
expect(cache.entries()).toHaveLength(0);
|
|
58
|
+
});
|
|
59
|
+
it("survives a corrupt manifest", () => {
|
|
60
|
+
const directory = temporaryDirectory();
|
|
61
|
+
writeFileSync(join(directory, "build-cache.json"), "{ not json");
|
|
62
|
+
const cache = new BuildCache(directory);
|
|
63
|
+
expect(cache.entries()).toHaveLength(0);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
//# sourceMappingURL=cache.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.test.js","sourceRoot":"","sources":["../../../tests/driver/cache.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
5
|
+
import { build, compileString } from "../../src/driver/compiler.js";
|
|
6
|
+
const directories = [];
|
|
7
|
+
function temporaryDirectory() {
|
|
8
|
+
const directory = mkdtempSync(join(tmpdir(), "xbintsc-build-"));
|
|
9
|
+
directories.push(directory);
|
|
10
|
+
return directory;
|
|
11
|
+
}
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
while (directories.length > 0)
|
|
14
|
+
rmSync(directories.pop(), { recursive: true, force: true });
|
|
15
|
+
});
|
|
16
|
+
describe("compileString", () => {
|
|
17
|
+
it("turns source into LLVM IR", () => {
|
|
18
|
+
const { ir, diagnostics } = compileString("console.log(1 + 2);", "inline.ts");
|
|
19
|
+
expect(diagnostics.filter((d) => d.category === "error")).toHaveLength(0);
|
|
20
|
+
expect(ir).toContain("define i32 @main");
|
|
21
|
+
expect(ir).toContain("@xt_add");
|
|
22
|
+
});
|
|
23
|
+
it("reports parse errors without producing a broken module", () => {
|
|
24
|
+
const { diagnostics } = compileString("const = ;", "bad.ts");
|
|
25
|
+
expect(diagnostics.some((d) => d.category === "error")).toBe(true);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
describe("build", () => {
|
|
29
|
+
it("emits an IR file and skips work on the second run", () => {
|
|
30
|
+
const directory = temporaryDirectory();
|
|
31
|
+
const entry = join(directory, "main.ts");
|
|
32
|
+
writeFileSync(entry, "console.log(21 * 2);");
|
|
33
|
+
const outDir = join(directory, "out");
|
|
34
|
+
const first = build(entry, { emit: "ir", outDir, cacheDir: join(directory, ".cache") });
|
|
35
|
+
expect(first.cached).toBe(false);
|
|
36
|
+
expect(first.outputPath.endsWith(".ll")).toBe(true);
|
|
37
|
+
expect(existsSync(first.outputPath)).toBe(true);
|
|
38
|
+
expect(readFileSync(first.outputPath, "utf8")).toContain("@xt_mul");
|
|
39
|
+
const second = build(entry, { emit: "ir", outDir, cacheDir: join(directory, ".cache") });
|
|
40
|
+
expect(second.cached).toBe(true);
|
|
41
|
+
const forced = build(entry, { emit: "ir", outDir, cacheDir: join(directory, ".cache"), force: true });
|
|
42
|
+
expect(forced.cached).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
it("returns diagnostics for invalid programs", () => {
|
|
45
|
+
const directory = temporaryDirectory();
|
|
46
|
+
const entry = join(directory, "bad.ts");
|
|
47
|
+
writeFileSync(entry, "const = ;");
|
|
48
|
+
const result = build(entry, { emit: "ir", outDir: join(directory, "out"), cacheDir: join(directory, ".cache") });
|
|
49
|
+
expect(result.diagnostics.some((d) => d.category === "error")).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
it("drives clang through the toolchain to link an executable", () => {
|
|
52
|
+
const directory = temporaryDirectory();
|
|
53
|
+
const entry = join(directory, "main.ts");
|
|
54
|
+
writeFileSync(entry, "console.log(1);");
|
|
55
|
+
const calls = [];
|
|
56
|
+
const runner = {
|
|
57
|
+
run(command, args) {
|
|
58
|
+
calls.push({ command, args: [...args] });
|
|
59
|
+
return { status: 0, stdout: "", stderr: "" };
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
const result = build(entry, {
|
|
63
|
+
emit: "exe",
|
|
64
|
+
outDir: join(directory, "out"),
|
|
65
|
+
cacheDir: join(directory, ".cache"),
|
|
66
|
+
runner,
|
|
67
|
+
clang: "clang",
|
|
68
|
+
});
|
|
69
|
+
expect(result.diagnostics).toHaveLength(0);
|
|
70
|
+
// clang was used for the runtime object, the module object and the link.
|
|
71
|
+
const compileCalls = calls.filter((call) => call.args.includes("-c"));
|
|
72
|
+
expect(compileCalls.length).toBeGreaterThanOrEqual(2);
|
|
73
|
+
const linkCall = calls.at(-1);
|
|
74
|
+
expect(linkCall.args).toContain("-o");
|
|
75
|
+
expect(linkCall.args).toContain(result.outputPath);
|
|
76
|
+
if (process.platform !== "win32")
|
|
77
|
+
expect(linkCall.args).toContain("-lm");
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
//# sourceMappingURL=compiler.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compiler.test.js","sourceRoot":"","sources":["../../../tests/driver/compiler.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { compileC, compileIr, findClang, link, ToolchainError, } from "../../src/driver/toolchain.js";
|
|
3
|
+
function recordingRunner(status = 0) {
|
|
4
|
+
const calls = [];
|
|
5
|
+
const runner = {
|
|
6
|
+
run(command, args) {
|
|
7
|
+
calls.push({ command, args: [...args] });
|
|
8
|
+
return { status, stdout: "", stderr: status === 0 ? "" : "boom" };
|
|
9
|
+
},
|
|
10
|
+
};
|
|
11
|
+
return { runner, calls };
|
|
12
|
+
}
|
|
13
|
+
describe("toolchain", () => {
|
|
14
|
+
it("honours xbintsc_CLANG when locating a compiler", () => {
|
|
15
|
+
const { runner, calls } = recordingRunner();
|
|
16
|
+
const previous = process.env.xbintsc_CLANG;
|
|
17
|
+
process.env.xbintsc_CLANG = "my-clang";
|
|
18
|
+
try {
|
|
19
|
+
expect(findClang(runner)).toBe("my-clang");
|
|
20
|
+
expect(calls[0].command).toBe("my-clang");
|
|
21
|
+
}
|
|
22
|
+
finally {
|
|
23
|
+
if (previous === undefined)
|
|
24
|
+
delete process.env.xbintsc_CLANG;
|
|
25
|
+
else
|
|
26
|
+
process.env.xbintsc_CLANG = previous;
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
it("fails loudly when no compiler responds", () => {
|
|
30
|
+
const runner = { run: () => ({ status: 127, stdout: "", stderr: "not found" }) };
|
|
31
|
+
expect(() => findClang(runner)).toThrow(ToolchainError);
|
|
32
|
+
});
|
|
33
|
+
it("compiles IR with the requested optimization level", () => {
|
|
34
|
+
const { runner, calls } = recordingRunner();
|
|
35
|
+
compileIr(runner, { clang: "clang", irPath: "in.ll", objectPath: "out.o", optimize: "2" });
|
|
36
|
+
expect(calls[0].args).toEqual(["-O2", "-c", "in.ll", "-o", "out.o"]);
|
|
37
|
+
});
|
|
38
|
+
it("links objects with extra flags", () => {
|
|
39
|
+
const { runner, calls } = recordingRunner();
|
|
40
|
+
link(runner, { clang: "clang", objectPaths: ["a.o", "b.o"], outputPath: "prog", linkerFlags: ["-lm"], optimize: "1" });
|
|
41
|
+
expect(calls[0].args).toEqual(["-O1", "a.o", "b.o", "-o", "prog", "-lm"]);
|
|
42
|
+
});
|
|
43
|
+
it("compiles C sources with an include directory", () => {
|
|
44
|
+
const { runner, calls } = recordingRunner();
|
|
45
|
+
compileC(runner, "clang", "runtime.c", "runtime.o", "runtime");
|
|
46
|
+
expect(calls[0].args).toEqual(["-O2", "-c", "runtime.c", "-o", "runtime.o", "-Iruntime"]);
|
|
47
|
+
});
|
|
48
|
+
it("throws a ToolchainError carrying stderr on failure", () => {
|
|
49
|
+
const { runner } = recordingRunner(1);
|
|
50
|
+
try {
|
|
51
|
+
compileIr(runner, { clang: "clang", irPath: "in.ll", objectPath: "out.o", optimize: "2" });
|
|
52
|
+
expect.unreachable();
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
expect(error).toBeInstanceOf(ToolchainError);
|
|
56
|
+
expect(error.stderr).toBe("boom");
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
//# sourceMappingURL=toolchain.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolchain.test.js","sourceRoot":"","sources":["../../../tests/driver/toolchain.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* End-to-end tests: compile TypeScript all the way to a native binary and run
|
|
3
|
+
* it. Skipped automatically when no clang-compatible compiler is available.
|
|
4
|
+
*/
|
|
5
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { tmpdir } from "node:os";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { spawnSync } from "node:child_process";
|
|
9
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
10
|
+
import { build } from "../../src/driver/compiler.js";
|
|
11
|
+
import { createDefaultRegistry } from "../../src/extensions/registry.js";
|
|
12
|
+
import { nodeExtension } from "../../src/extensions/node.js";
|
|
13
|
+
import { hasClang } from "../helpers.js";
|
|
14
|
+
const describeWithClang = hasClang() ? describe : describe.skip;
|
|
15
|
+
describeWithClang("end-to-end compilation", () => {
|
|
16
|
+
let workdir;
|
|
17
|
+
let cacheDir;
|
|
18
|
+
beforeAll(() => {
|
|
19
|
+
workdir = mkdtempSync(join(tmpdir(), "xbintsc-e2e-"));
|
|
20
|
+
cacheDir = join(workdir, ".cache");
|
|
21
|
+
});
|
|
22
|
+
afterAll(() => {
|
|
23
|
+
rmSync(workdir, { recursive: true, force: true });
|
|
24
|
+
});
|
|
25
|
+
function runProgram(source, options = {}) {
|
|
26
|
+
const name = options.name ?? `program_${Math.random().toString(36).slice(2)}`;
|
|
27
|
+
const entry = join(workdir, `${name}.ts`);
|
|
28
|
+
writeFileSync(entry, source);
|
|
29
|
+
const extensions = options.extensions ? createDefaultRegistry().register(nodeExtension) : undefined;
|
|
30
|
+
const result = build(entry, {
|
|
31
|
+
emit: "exe",
|
|
32
|
+
outDir: join(workdir, "out"),
|
|
33
|
+
cacheDir,
|
|
34
|
+
extensions,
|
|
35
|
+
});
|
|
36
|
+
expect(result.diagnostics.filter((d) => d.category === "error")).toEqual([]);
|
|
37
|
+
const executed = spawnSync(result.outputPath, [], { encoding: "utf8" });
|
|
38
|
+
expect(executed.status).toBe(0);
|
|
39
|
+
return executed.stdout.trim();
|
|
40
|
+
}
|
|
41
|
+
it("evaluates arithmetic and prints results", () => {
|
|
42
|
+
expect(runProgram('console.log("sum", 2 + 3 * 4);')).toBe("sum 14");
|
|
43
|
+
});
|
|
44
|
+
it("runs recursive functions", () => {
|
|
45
|
+
const source = `
|
|
46
|
+
function fib(n: number): number {
|
|
47
|
+
if (n < 2) return n;
|
|
48
|
+
return fib(n - 1) + fib(n - 2);
|
|
49
|
+
}
|
|
50
|
+
console.log(fib(10));
|
|
51
|
+
`;
|
|
52
|
+
expect(runProgram(source)).toBe("55");
|
|
53
|
+
});
|
|
54
|
+
it("supports loops, arrays and string concatenation", () => {
|
|
55
|
+
const source = `
|
|
56
|
+
const numbers = [1, 2, 3, 4];
|
|
57
|
+
let total = 0;
|
|
58
|
+
for (const n of numbers) {
|
|
59
|
+
total += n;
|
|
60
|
+
}
|
|
61
|
+
console.log("total = " + total);
|
|
62
|
+
`;
|
|
63
|
+
expect(runProgram(source)).toBe("total = 10");
|
|
64
|
+
});
|
|
65
|
+
it("implements closures with by-reference captures", () => {
|
|
66
|
+
const source = `
|
|
67
|
+
const makeCounter = () => {
|
|
68
|
+
let count = 0;
|
|
69
|
+
return () => {
|
|
70
|
+
count += 1;
|
|
71
|
+
return count;
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
const next = makeCounter();
|
|
75
|
+
console.log(next(), next(), next());
|
|
76
|
+
`;
|
|
77
|
+
expect(runProgram(source)).toBe("1 2 3");
|
|
78
|
+
});
|
|
79
|
+
it("prints objects and arrays with JS-like formatting", () => {
|
|
80
|
+
const source = `
|
|
81
|
+
const user = { name: "ada", tags: ["math", "code"], age: 36 };
|
|
82
|
+
console.log(user);
|
|
83
|
+
`;
|
|
84
|
+
expect(runProgram(source)).toBe("{ name: ada, tags: [ math, code ], age: 36 }");
|
|
85
|
+
});
|
|
86
|
+
it("reads files through the node extension", () => {
|
|
87
|
+
const dataPath = join(workdir, "data.txt");
|
|
88
|
+
writeFileSync(dataPath, "from a file");
|
|
89
|
+
const source = `console.log(readFileSync(${JSON.stringify(dataPath)}));`;
|
|
90
|
+
expect(runProgram(source, { extensions: true })).toBe("from a file");
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
//# sourceMappingURL=compile.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compile.test.js","sourceRoot":"","sources":["../../../tests/e2e/compile.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { ExtensionRegistry, createDefaultRegistry, coreExtension } from "../../src/extensions/registry.js";
|
|
3
|
+
import { nodeExtension } from "../../src/extensions/node.js";
|
|
4
|
+
const demoExtension = {
|
|
5
|
+
name: "demo",
|
|
6
|
+
builtins: () => ({ demo: { symbol: "xt_demo" }, ping: { symbol: "xt_ping", returnVoid: true } }),
|
|
7
|
+
runtimeSources: () => ["/tmp/demo.c"],
|
|
8
|
+
linkerFlags: () => ["-ldemo"],
|
|
9
|
+
};
|
|
10
|
+
describe("ExtensionRegistry", () => {
|
|
11
|
+
it("registers and retrieves extensions", () => {
|
|
12
|
+
const registry = new ExtensionRegistry().register(demoExtension);
|
|
13
|
+
expect(registry.has("demo")).toBe(true);
|
|
14
|
+
expect(registry.get("demo")).toBe(demoExtension);
|
|
15
|
+
expect(registry.all()).toHaveLength(1);
|
|
16
|
+
});
|
|
17
|
+
it("rejects duplicate names", () => {
|
|
18
|
+
const registry = new ExtensionRegistry().register(demoExtension);
|
|
19
|
+
expect(() => registry.register(demoExtension)).toThrow(/already registered/);
|
|
20
|
+
});
|
|
21
|
+
it("unregisters extensions", () => {
|
|
22
|
+
const registry = new ExtensionRegistry().register(demoExtension);
|
|
23
|
+
expect(registry.unregister("demo")).toBe(true);
|
|
24
|
+
expect(registry.unregister("demo")).toBe(false);
|
|
25
|
+
});
|
|
26
|
+
it("merges builtins from every extension", () => {
|
|
27
|
+
const registry = createDefaultRegistry().register(nodeExtension);
|
|
28
|
+
const builtins = registry.builtins();
|
|
29
|
+
expect(builtins.print?.symbol).toBe("xt_println");
|
|
30
|
+
expect(builtins.readFileSync?.symbol).toBe("xt_node_read_text_file");
|
|
31
|
+
});
|
|
32
|
+
it("collects runtime sources and linker flags", () => {
|
|
33
|
+
const registry = new ExtensionRegistry().register(demoExtension);
|
|
34
|
+
expect(registry.runtimeSources()).toEqual(["/tmp/demo.c"]);
|
|
35
|
+
expect(registry.linkerFlags()).toEqual(["-ldemo"]);
|
|
36
|
+
});
|
|
37
|
+
it("ships a core extension exposing print", () => {
|
|
38
|
+
expect(coreExtension.builtins?.().print?.symbol).toBe("xt_println");
|
|
39
|
+
expect(createDefaultRegistry().has("core")).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
it("node extension points at the ext C source", () => {
|
|
42
|
+
const sources = nodeExtension.runtimeSources?.() ?? [];
|
|
43
|
+
expect(sources).toHaveLength(1);
|
|
44
|
+
expect(sources[0]).toMatch(/ext_node\.c$/);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
//# sourceMappingURL=registry.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.test.js","sourceRoot":"","sources":["../../../tests/extensions/registry.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Shared helpers for the per-module test suites. */
|
|
2
|
+
import { type Diagnostic } from "../src/diagnostics/diagnostic.js";
|
|
3
|
+
import { type BindResult } from "../src/binder/binder.js";
|
|
4
|
+
import { type ExtensionRegistry } from "../src/extensions/registry.js";
|
|
5
|
+
import type { Token } from "../src/lexer/token.js";
|
|
6
|
+
export declare function lex(text: string): {
|
|
7
|
+
tokens: Token[];
|
|
8
|
+
diagnostics: readonly Diagnostic[];
|
|
9
|
+
};
|
|
10
|
+
export declare function parse(text: string): {
|
|
11
|
+
file: import("../src/index.js").SourceFileNode;
|
|
12
|
+
diagnostics: readonly Diagnostic[];
|
|
13
|
+
};
|
|
14
|
+
export declare function bindSource(text: string): {
|
|
15
|
+
result: BindResult;
|
|
16
|
+
diagnostics: readonly Diagnostic[];
|
|
17
|
+
};
|
|
18
|
+
export declare function compileToIr(text: string, extensions?: ExtensionRegistry): {
|
|
19
|
+
ir: string;
|
|
20
|
+
binding: BindResult;
|
|
21
|
+
diagnostics: readonly Diagnostic[];
|
|
22
|
+
};
|
|
23
|
+
export declare function hasClang(): boolean;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** Shared helpers for the per-module test suites. */
|
|
2
|
+
import { DiagnosticBag } from "../src/diagnostics/diagnostic.js";
|
|
3
|
+
import { SourceFile } from "../src/diagnostics/source.js";
|
|
4
|
+
import { Scanner } from "../src/lexer/scanner.js";
|
|
5
|
+
import { Parser } from "../src/parser/parser.js";
|
|
6
|
+
import { bind } from "../src/binder/binder.js";
|
|
7
|
+
import { generate } from "../src/codegen/llvm.js";
|
|
8
|
+
import { createDefaultRegistry } from "../src/extensions/registry.js";
|
|
9
|
+
import { realRunner } from "../src/driver/toolchain.js";
|
|
10
|
+
export function lex(text) {
|
|
11
|
+
const source = new SourceFile("test.ts", text);
|
|
12
|
+
const diagnostics = new DiagnosticBag();
|
|
13
|
+
const scanner = new Scanner(source, diagnostics);
|
|
14
|
+
return { tokens: scanner.tokenize(), diagnostics: diagnostics.diagnostics };
|
|
15
|
+
}
|
|
16
|
+
export function parse(text) {
|
|
17
|
+
const source = new SourceFile("test.ts", text);
|
|
18
|
+
const diagnostics = new DiagnosticBag();
|
|
19
|
+
const parser = new Parser(source, diagnostics);
|
|
20
|
+
const file = parser.parseSourceFile();
|
|
21
|
+
return { file, diagnostics: diagnostics.diagnostics };
|
|
22
|
+
}
|
|
23
|
+
export function bindSource(text) {
|
|
24
|
+
const { file, diagnostics } = parse(text);
|
|
25
|
+
const bag = new DiagnosticBag();
|
|
26
|
+
bag.addAll(diagnostics);
|
|
27
|
+
const result = bind(file);
|
|
28
|
+
return { result, diagnostics: bag.diagnostics };
|
|
29
|
+
}
|
|
30
|
+
export function compileToIr(text, extensions) {
|
|
31
|
+
const { file, diagnostics: parseDiagnostics } = parse(text);
|
|
32
|
+
const diagnostics = new DiagnosticBag();
|
|
33
|
+
diagnostics.addAll(parseDiagnostics);
|
|
34
|
+
const registry = extensions ?? createDefaultRegistry();
|
|
35
|
+
const { ir, binding } = generate(file, diagnostics, { builtins: registry.builtins() });
|
|
36
|
+
return { ir, binding, diagnostics: diagnostics.diagnostics };
|
|
37
|
+
}
|
|
38
|
+
export function hasClang() {
|
|
39
|
+
return realRunner.run(process.env.xbintsc_CLANG ?? "clang", ["--version"]).status === 0;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../tests/helpers.ts"],"names":[],"mappings":"AAAA,qDAAqD;AAErD,OAAO,EAAE,aAAa,EAAmB,MAAM,kCAAkC,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,IAAI,EAAmB,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,qBAAqB,EAA0B,MAAM,+BAA+B,CAAC;AAC9F,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAGxD,MAAM,UAAU,GAAG,CAAC,IAAY;IAC9B,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC/C,MAAM,WAAW,GAAG,IAAI,aAAa,EAAE,CAAC;IACxC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACjD,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,QAAQ,EAAE,EAAE,WAAW,EAAE,WAAW,CAAC,WAAW,EAAE,CAAC;AAC9E,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,IAAY;IAChC,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC/C,MAAM,WAAW,GAAG,IAAI,aAAa,EAAE,CAAC;IACxC,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC/C,MAAM,IAAI,GAAG,MAAM,CAAC,eAAe,EAAE,CAAC;IACtC,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,CAAC,WAAW,EAAE,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1C,MAAM,GAAG,GAAG,IAAI,aAAa,EAAE,CAAC;IAChC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IACxB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,CAAC,WAAW,EAAE,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,UAA8B;IACtE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,gBAAgB,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5D,MAAM,WAAW,GAAG,IAAI,aAAa,EAAE,CAAC;IACxC,WAAW,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAG,UAAU,IAAI,qBAAqB,EAAE,CAAC;IACvD,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,QAAQ,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACvF,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,CAAC,WAAW,EAAE,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,OAAO,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,OAAO,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;AAC1F,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { lex } from "../helpers.js";
|
|
3
|
+
import { TokenKind } from "../../src/lexer/token.js";
|
|
4
|
+
function kinds(text) {
|
|
5
|
+
return lex(text).tokens.map((token) => token.kind);
|
|
6
|
+
}
|
|
7
|
+
function texts(text) {
|
|
8
|
+
return lex(text).tokens.map((token) => token.text);
|
|
9
|
+
}
|
|
10
|
+
describe("scanner", () => {
|
|
11
|
+
it("always terminates with an EOF token", () => {
|
|
12
|
+
const { tokens } = lex("");
|
|
13
|
+
expect(tokens).toHaveLength(1);
|
|
14
|
+
expect(tokens[0].kind).toBe(TokenKind.EndOfFile);
|
|
15
|
+
});
|
|
16
|
+
it("distinguishes keywords from identifiers", () => {
|
|
17
|
+
expect(kinds("const x = 1")).toEqual([
|
|
18
|
+
TokenKind.ConstKeyword,
|
|
19
|
+
TokenKind.Identifier,
|
|
20
|
+
TokenKind.Equals,
|
|
21
|
+
TokenKind.NumericLiteral,
|
|
22
|
+
TokenKind.EndOfFile,
|
|
23
|
+
]);
|
|
24
|
+
expect(kinds("constant")).toEqual([TokenKind.Identifier, TokenKind.EndOfFile]);
|
|
25
|
+
});
|
|
26
|
+
it("scans every numeric literal form", () => {
|
|
27
|
+
const { tokens, diagnostics } = lex("0 42 3.14 .5 1e3 1E-3 0xFF 0b1010 0o17 1_000");
|
|
28
|
+
expect(diagnostics).toHaveLength(0);
|
|
29
|
+
const literals = tokens.filter((t) => t.kind === TokenKind.NumericLiteral).map((t) => t.text);
|
|
30
|
+
expect(literals).toEqual(["0", "42", "3.14", ".5", "1e3", "1E-3", "0xFF", "0b1010", "0o17", "1_000"]);
|
|
31
|
+
});
|
|
32
|
+
it("scans strings with escapes", () => {
|
|
33
|
+
const { tokens, diagnostics } = lex('"a\\n\\t\\"b" \'c\'');
|
|
34
|
+
expect(diagnostics).toHaveLength(0);
|
|
35
|
+
const literals = tokens.filter((t) => t.kind === TokenKind.StringLiteral);
|
|
36
|
+
expect(literals).toHaveLength(2);
|
|
37
|
+
expect(literals[0].text).toBe('"a\\n\\t\\"b"');
|
|
38
|
+
});
|
|
39
|
+
it("reports unterminated strings", () => {
|
|
40
|
+
const { diagnostics } = lex('"oops');
|
|
41
|
+
expect(diagnostics.some((d) => d.category === "error")).toBe(true);
|
|
42
|
+
});
|
|
43
|
+
it("scans template literal heads and template text", () => {
|
|
44
|
+
const { tokens } = lex("`a${");
|
|
45
|
+
expect(tokens[0].kind).toBe(TokenKind.TemplateHead);
|
|
46
|
+
expect(tokens[0].text).toBe("`a${");
|
|
47
|
+
});
|
|
48
|
+
it("scans a no-substitution template as a single token", () => {
|
|
49
|
+
const { tokens } = lex("`plain`");
|
|
50
|
+
expect(tokens[0].kind).toBe(TokenKind.NoSubstitutionTemplateLiteral);
|
|
51
|
+
expect(tokens[0].text).toBe("`plain`");
|
|
52
|
+
});
|
|
53
|
+
it("scans operators greedily", () => {
|
|
54
|
+
expect(kinds("a === b !== c ?? d => e")).toEqual([
|
|
55
|
+
TokenKind.Identifier,
|
|
56
|
+
TokenKind.EqualsEqualsEquals,
|
|
57
|
+
TokenKind.Identifier,
|
|
58
|
+
TokenKind.ExclamationEqualsEquals,
|
|
59
|
+
TokenKind.Identifier,
|
|
60
|
+
TokenKind.QuestionQuestion,
|
|
61
|
+
TokenKind.Identifier,
|
|
62
|
+
TokenKind.EqualsGreaterThan,
|
|
63
|
+
TokenKind.Identifier,
|
|
64
|
+
TokenKind.EndOfFile,
|
|
65
|
+
]);
|
|
66
|
+
});
|
|
67
|
+
it("distinguishes regex literals from division", () => {
|
|
68
|
+
const regex = lex("const r = /ab+c/gi;");
|
|
69
|
+
const regexToken = regex.tokens.find((t) => t.kind === TokenKind.RegularExpressionLiteral);
|
|
70
|
+
expect(regexToken?.text).toBe("/ab+c/gi");
|
|
71
|
+
expect(regexToken?.kind).toBe(TokenKind.RegularExpressionLiteral);
|
|
72
|
+
const division = lex("const q = a / b / c;");
|
|
73
|
+
expect(division.tokens.filter((t) => t.kind === TokenKind.Slash)).toHaveLength(2);
|
|
74
|
+
});
|
|
75
|
+
it("skips line and block comments", () => {
|
|
76
|
+
const { tokens, diagnostics } = lex("// line\n/* block */ x");
|
|
77
|
+
expect(diagnostics).toHaveLength(0);
|
|
78
|
+
expect(tokens[0].kind).toBe(TokenKind.Identifier);
|
|
79
|
+
});
|
|
80
|
+
it("tracks whether a token is preceded by a line break", () => {
|
|
81
|
+
const { tokens } = lex("a\nb c");
|
|
82
|
+
const identifiers = tokens.filter((t) => t.kind === TokenKind.Identifier);
|
|
83
|
+
expect(identifiers[0].precededByLineBreak).toBe(false);
|
|
84
|
+
expect(identifiers[1].precededByLineBreak).toBe(true);
|
|
85
|
+
expect(identifiers[2].precededByLineBreak).toBe(false);
|
|
86
|
+
});
|
|
87
|
+
it("normalizes BOM and CRLF through SourceFile", () => {
|
|
88
|
+
expect(texts("\uFEFFa\r\nb")).toEqual(["a", "b", ""]);
|
|
89
|
+
});
|
|
90
|
+
it("reports unterminated block comments", () => {
|
|
91
|
+
const { diagnostics } = lex("/* never ends");
|
|
92
|
+
expect(diagnostics.map((d) => d.code)).toContain(1003);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
//# sourceMappingURL=scanner.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.test.js","sourceRoot":"","sources":["../../../tests/lexer/scanner.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|