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
package/src/cli/main.ts
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command line interface.
|
|
3
|
+
*
|
|
4
|
+
* Commands are intentionally small and composable so the same operations are
|
|
5
|
+
* available programmatically through the driver API:
|
|
6
|
+
*
|
|
7
|
+
* xbintsc build <file> [-o out] [--emit ir|obj|exe] [-O0..3] [--ext node]
|
|
8
|
+
* xbintsc run <file> [-- args...]
|
|
9
|
+
* xbintsc emit <file> # print LLVM IR to stdout
|
|
10
|
+
* xbintsc version
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { spawnSync } from "node:child_process";
|
|
14
|
+
import { readFileSync } from "node:fs";
|
|
15
|
+
import { resolve } from "node:path";
|
|
16
|
+
import { pathToFileURL } from "node:url";
|
|
17
|
+
import { DiagnosticBag, formatDiagnostic, type Diagnostic } from "../diagnostics/diagnostic.js";
|
|
18
|
+
import { SourceFile } from "../diagnostics/source.js";
|
|
19
|
+
import { build, compileString, COMPILER_VERSION, type EmitKind } from "../driver/compiler.js";
|
|
20
|
+
import { createDefaultRegistry, type ExtensionRegistry } from "../extensions/registry.js";
|
|
21
|
+
import { nodeExtension } from "../extensions/node.js";
|
|
22
|
+
|
|
23
|
+
export interface CliIo {
|
|
24
|
+
readonly stdout: (text: string) => void;
|
|
25
|
+
readonly stderr: (text: string) => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const defaultIo: CliIo = {
|
|
29
|
+
stdout: (text) => process.stdout.write(text),
|
|
30
|
+
stderr: (text) => process.stderr.write(text),
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
interface ParsedArgs {
|
|
34
|
+
readonly command?: string;
|
|
35
|
+
readonly positionals: string[];
|
|
36
|
+
readonly flags: Map<string, string | boolean>;
|
|
37
|
+
readonly passthrough: string[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const VALUE_FLAGS = new Set(["output", "out", "emit", "optimize", "ext"]);
|
|
41
|
+
|
|
42
|
+
function parseArgs(argv: readonly string[]): ParsedArgs {
|
|
43
|
+
const positionals: string[] = [];
|
|
44
|
+
const flags = new Map<string, string | boolean>();
|
|
45
|
+
const passthrough: string[] = [];
|
|
46
|
+
let afterSeparator = false;
|
|
47
|
+
for (let index = 0; index < argv.length; index++) {
|
|
48
|
+
const arg = argv[index]!;
|
|
49
|
+
if (afterSeparator) {
|
|
50
|
+
passthrough.push(arg);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (arg === "--") {
|
|
54
|
+
afterSeparator = true;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (arg.startsWith("--")) {
|
|
58
|
+
const [name, inlineValue] = arg.slice(2).split("=", 2);
|
|
59
|
+
if (inlineValue !== undefined) {
|
|
60
|
+
flags.set(name!, inlineValue);
|
|
61
|
+
} else if (VALUE_FLAGS.has(name!)) {
|
|
62
|
+
const next = argv[index + 1];
|
|
63
|
+
if (next !== undefined && !next.startsWith("-")) {
|
|
64
|
+
flags.set(name!, next);
|
|
65
|
+
index++;
|
|
66
|
+
} else {
|
|
67
|
+
flags.set(name!, true);
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
flags.set(name!, true);
|
|
71
|
+
}
|
|
72
|
+
} else if (arg.startsWith("-") && arg.length > 1 && !/^-\d/.test(arg)) {
|
|
73
|
+
const name = arg.slice(1);
|
|
74
|
+
if (name.startsWith("O")) {
|
|
75
|
+
flags.set("optimize", name.slice(1) || "2");
|
|
76
|
+
} else if (name === "o") {
|
|
77
|
+
const next = argv[index + 1];
|
|
78
|
+
if (next !== undefined) {
|
|
79
|
+
flags.set("output", next);
|
|
80
|
+
index++;
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
flags.set(name, true);
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
positionals.push(arg);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return { command: positionals.shift(), positionals, flags, passthrough };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function buildRegistry(flags: Map<string, string | boolean>): ExtensionRegistry {
|
|
93
|
+
const registry = createDefaultRegistry();
|
|
94
|
+
const requested = flags.get("ext");
|
|
95
|
+
if (typeof requested === "string") {
|
|
96
|
+
for (const name of requested.split(",").map((n) => n.trim()).filter(Boolean)) {
|
|
97
|
+
if (name === "node") registry.register(nodeExtension);
|
|
98
|
+
else throw new Error(`Unknown extension '${name}'`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return registry;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function printDiagnostics(diagnostics: readonly Diagnostic[], fileName: string, io: CliIo): void {
|
|
105
|
+
let source: SourceFile | undefined;
|
|
106
|
+
try {
|
|
107
|
+
source = new SourceFile(fileName, readFileSync(fileName, "utf8"));
|
|
108
|
+
} catch {
|
|
109
|
+
source = undefined;
|
|
110
|
+
}
|
|
111
|
+
for (const diagnostic of diagnostics) {
|
|
112
|
+
io.stderr(formatDiagnostic(diagnostic, source) + "\n");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const HELP = `xbintsc ${COMPILER_VERSION} - TypeScript binary compiler
|
|
117
|
+
|
|
118
|
+
Usage:
|
|
119
|
+
xbintsc build <file.ts> [options] Compile to a native binary
|
|
120
|
+
xbintsc run <file.ts> [-- args] Compile and execute
|
|
121
|
+
xbintsc emit <file.ts> Print LLVM IR
|
|
122
|
+
xbintsc version Print the version
|
|
123
|
+
xbintsc help Show this message
|
|
124
|
+
|
|
125
|
+
Options:
|
|
126
|
+
-o, --output <path> Output path
|
|
127
|
+
--out <dir> Output directory (default: build/)
|
|
128
|
+
--emit <kind> exe | obj | ir (default: exe)
|
|
129
|
+
-O0..-O3 Optimization level (default: -O2)
|
|
130
|
+
--ext <names> Comma separated extensions (e.g. node)
|
|
131
|
+
--force Ignore the incremental cache
|
|
132
|
+
--verbose Print progress information
|
|
133
|
+
`;
|
|
134
|
+
|
|
135
|
+
export function run(argv: readonly string[], io: CliIo = defaultIo): number {
|
|
136
|
+
const args = parseArgs(argv);
|
|
137
|
+
const command = args.command;
|
|
138
|
+
|
|
139
|
+
if (!command || command === "help" || args.flags.has("help")) {
|
|
140
|
+
io.stdout(HELP);
|
|
141
|
+
return 0;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (command === "version" || args.flags.has("version")) {
|
|
145
|
+
io.stdout(`xbintsc ${COMPILER_VERSION}\n`);
|
|
146
|
+
return 0;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (command === "emit") {
|
|
150
|
+
const entry = args.positionals[0];
|
|
151
|
+
if (!entry) {
|
|
152
|
+
io.stderr("xbintsc: emit requires a source file\n");
|
|
153
|
+
return 1;
|
|
154
|
+
}
|
|
155
|
+
const source = readFileSync(resolve(entry), "utf8");
|
|
156
|
+
const registry = buildRegistry(args.flags);
|
|
157
|
+
const { ir, diagnostics } = compileString(source, entry, registry);
|
|
158
|
+
if (diagnostics.some((d) => d.category === "error")) {
|
|
159
|
+
printDiagnostics(diagnostics, entry, io);
|
|
160
|
+
return 1;
|
|
161
|
+
}
|
|
162
|
+
io.stdout(ir);
|
|
163
|
+
return 0;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (command === "build" || command === "run") {
|
|
167
|
+
const entry = args.positionals[0];
|
|
168
|
+
if (!entry) {
|
|
169
|
+
io.stderr(`xbintsc: ${command} requires a source file\n`);
|
|
170
|
+
return 1;
|
|
171
|
+
}
|
|
172
|
+
const emit = (args.flags.get("emit") as EmitKind | undefined) ?? "exe";
|
|
173
|
+
const optimize = (args.flags.get("optimize") as string | undefined) ?? "2";
|
|
174
|
+
const result = build(entry, {
|
|
175
|
+
output: typeof args.flags.get("output") === "string" ? (args.flags.get("output") as string) : undefined,
|
|
176
|
+
outDir: typeof args.flags.get("out") === "string" ? (args.flags.get("out") as string) : undefined,
|
|
177
|
+
emit,
|
|
178
|
+
optimize: optimize as "0" | "1" | "2" | "3",
|
|
179
|
+
force: Boolean(args.flags.get("force")),
|
|
180
|
+
verbose: Boolean(args.flags.get("verbose")),
|
|
181
|
+
extensions: buildRegistry(args.flags),
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
if (result.diagnostics.some((d) => d.category === "error")) {
|
|
185
|
+
printDiagnostics(result.diagnostics, entry, io);
|
|
186
|
+
return 1;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (command === "build") {
|
|
190
|
+
io.stdout(`xbintsc: wrote ${result.outputPath}${result.cached ? " (cached)" : ""}\n`);
|
|
191
|
+
return 0;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (emit !== "exe") {
|
|
195
|
+
io.stderr("xbintsc: run requires --emit exe\n");
|
|
196
|
+
return 1;
|
|
197
|
+
}
|
|
198
|
+
const executed = spawnSync(result.outputPath, args.passthrough, { stdio: "inherit" });
|
|
199
|
+
return executed.status ?? 0;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
io.stderr(`xbintsc: unknown command '${command}'\n\n${HELP}`);
|
|
203
|
+
return 1;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const isMain =
|
|
207
|
+
process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
208
|
+
if (isMain) {
|
|
209
|
+
process.exit(run(process.argv.slice(2)));
|
|
210
|
+
}
|