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.
Files changed (124) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +191 -0
  3. package/bin/xbintsc.js +26 -0
  4. package/dist/src/ast/factory.d.ts +11 -0
  5. package/dist/src/ast/factory.js +36 -0
  6. package/dist/src/ast/factory.js.map +1 -0
  7. package/dist/src/ast/nodes.d.ts +848 -0
  8. package/dist/src/ast/nodes.js +247 -0
  9. package/dist/src/ast/nodes.js.map +1 -0
  10. package/dist/src/ast/visitor.d.ts +18 -0
  11. package/dist/src/ast/visitor.js +52 -0
  12. package/dist/src/ast/visitor.js.map +1 -0
  13. package/dist/src/binder/binder.d.ts +82 -0
  14. package/dist/src/binder/binder.js +443 -0
  15. package/dist/src/binder/binder.js.map +1 -0
  16. package/dist/src/cli/main.d.ts +16 -0
  17. package/dist/src/cli/main.js +191 -0
  18. package/dist/src/cli/main.js.map +1 -0
  19. package/dist/src/codegen/llvm.d.ts +31 -0
  20. package/dist/src/codegen/llvm.js +1178 -0
  21. package/dist/src/codegen/llvm.js.map +1 -0
  22. package/dist/src/codegen/values.d.ts +23 -0
  23. package/dist/src/codegen/values.js +32 -0
  24. package/dist/src/codegen/values.js.map +1 -0
  25. package/dist/src/diagnostics/diagnostic.d.ts +69 -0
  26. package/dist/src/diagnostics/diagnostic.js +104 -0
  27. package/dist/src/diagnostics/diagnostic.js.map +1 -0
  28. package/dist/src/diagnostics/source.d.ts +43 -0
  29. package/dist/src/diagnostics/source.js +73 -0
  30. package/dist/src/diagnostics/source.js.map +1 -0
  31. package/dist/src/driver/cache.d.ts +37 -0
  32. package/dist/src/driver/cache.js +82 -0
  33. package/dist/src/driver/cache.js.map +1 -0
  34. package/dist/src/driver/compiler.d.ts +41 -0
  35. package/dist/src/driver/compiler.js +128 -0
  36. package/dist/src/driver/compiler.js.map +1 -0
  37. package/dist/src/driver/index.d.ts +4 -0
  38. package/dist/src/driver/index.js +5 -0
  39. package/dist/src/driver/index.js.map +1 -0
  40. package/dist/src/driver/paths.d.ts +4 -0
  41. package/dist/src/driver/paths.js +22 -0
  42. package/dist/src/driver/paths.js.map +1 -0
  43. package/dist/src/driver/toolchain.d.ts +43 -0
  44. package/dist/src/driver/toolchain.js +76 -0
  45. package/dist/src/driver/toolchain.js.map +1 -0
  46. package/dist/src/extensions/node.d.ts +9 -0
  47. package/dist/src/extensions/node.js +20 -0
  48. package/dist/src/extensions/node.js.map +1 -0
  49. package/dist/src/extensions/registry.d.ts +45 -0
  50. package/dist/src/extensions/registry.js +76 -0
  51. package/dist/src/extensions/registry.js.map +1 -0
  52. package/dist/src/index.d.ts +18 -0
  53. package/dist/src/index.js +19 -0
  54. package/dist/src/index.js.map +1 -0
  55. package/dist/src/lexer/scanner.d.ts +48 -0
  56. package/dist/src/lexer/scanner.js +650 -0
  57. package/dist/src/lexer/scanner.js.map +1 -0
  58. package/dist/src/lexer/token.d.ts +170 -0
  59. package/dist/src/lexer/token.js +250 -0
  60. package/dist/src/lexer/token.js.map +1 -0
  61. package/dist/src/parser/parser.d.ts +127 -0
  62. package/dist/src/parser/parser.js +2334 -0
  63. package/dist/src/parser/parser.js.map +1 -0
  64. package/dist/tests/binder/binder.test.d.ts +1 -0
  65. package/dist/tests/binder/binder.test.js +89 -0
  66. package/dist/tests/binder/binder.test.js.map +1 -0
  67. package/dist/tests/cli/main.test.d.ts +1 -0
  68. package/dist/tests/cli/main.test.js +68 -0
  69. package/dist/tests/cli/main.test.js.map +1 -0
  70. package/dist/tests/cli/process.test.d.ts +9 -0
  71. package/dist/tests/cli/process.test.js +57 -0
  72. package/dist/tests/cli/process.test.js.map +1 -0
  73. package/dist/tests/codegen/llvm.test.d.ts +1 -0
  74. package/dist/tests/codegen/llvm.test.js +93 -0
  75. package/dist/tests/codegen/llvm.test.js.map +1 -0
  76. package/dist/tests/driver/cache.test.d.ts +1 -0
  77. package/dist/tests/driver/cache.test.js +66 -0
  78. package/dist/tests/driver/cache.test.js.map +1 -0
  79. package/dist/tests/driver/compiler.test.d.ts +1 -0
  80. package/dist/tests/driver/compiler.test.js +80 -0
  81. package/dist/tests/driver/compiler.test.js.map +1 -0
  82. package/dist/tests/driver/toolchain.test.d.ts +1 -0
  83. package/dist/tests/driver/toolchain.test.js +60 -0
  84. package/dist/tests/driver/toolchain.test.js.map +1 -0
  85. package/dist/tests/e2e/compile.test.d.ts +5 -0
  86. package/dist/tests/e2e/compile.test.js +93 -0
  87. package/dist/tests/e2e/compile.test.js.map +1 -0
  88. package/dist/tests/extensions/registry.test.d.ts +1 -0
  89. package/dist/tests/extensions/registry.test.js +47 -0
  90. package/dist/tests/extensions/registry.test.js.map +1 -0
  91. package/dist/tests/helpers.d.ts +23 -0
  92. package/dist/tests/helpers.js +41 -0
  93. package/dist/tests/helpers.js.map +1 -0
  94. package/dist/tests/lexer/scanner.test.d.ts +1 -0
  95. package/dist/tests/lexer/scanner.test.js +95 -0
  96. package/dist/tests/lexer/scanner.test.js.map +1 -0
  97. package/dist/tests/parser/parser.test.d.ts +1 -0
  98. package/dist/tests/parser/parser.test.js +137 -0
  99. package/dist/tests/parser/parser.test.js.map +1 -0
  100. package/package.json +49 -0
  101. package/runtime/ext_node.c +42 -0
  102. package/runtime/rt.h +177 -0
  103. package/runtime/xt_runtime.c +682 -0
  104. package/scripts/build-runtime.mjs +34 -0
  105. package/src/ast/factory.ts +37 -0
  106. package/src/ast/nodes.ts +1111 -0
  107. package/src/ast/visitor.ts +60 -0
  108. package/src/binder/binder.ts +549 -0
  109. package/src/cli/main.ts +210 -0
  110. package/src/codegen/llvm.ts +1324 -0
  111. package/src/codegen/values.ts +36 -0
  112. package/src/diagnostics/diagnostic.ts +132 -0
  113. package/src/diagnostics/source.ts +97 -0
  114. package/src/driver/cache.ts +96 -0
  115. package/src/driver/compiler.ts +197 -0
  116. package/src/driver/index.ts +4 -0
  117. package/src/driver/paths.ts +24 -0
  118. package/src/driver/toolchain.ts +103 -0
  119. package/src/extensions/node.ts +23 -0
  120. package/src/extensions/registry.ts +101 -0
  121. package/src/index.ts +33 -0
  122. package/src/lexer/scanner.ts +650 -0
  123. package/src/lexer/token.ts +266 -0
  124. package/src/parser/parser.ts +2396 -0
@@ -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
+ }