xbintsc 0.3.46 → 0.3.49

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 (91) hide show
  1. package/AGENTS.md +95 -0
  2. package/README.md +25 -0
  3. package/README.zh-CN.md +23 -0
  4. package/dist/src/cli/hints.d.ts +54 -0
  5. package/dist/src/cli/hints.js +165 -0
  6. package/dist/src/cli/hints.js.map +1 -0
  7. package/dist/src/cli/main.js +73 -9
  8. package/dist/src/cli/main.js.map +1 -1
  9. package/dist/src/codegen/generator/tables.d.ts +26 -0
  10. package/dist/src/codegen/generator/tables.js +64 -12
  11. package/dist/src/codegen/generator/tables.js.map +1 -1
  12. package/dist/src/diagnostics/source-text.d.ts +22 -0
  13. package/dist/src/diagnostics/source-text.js +76 -0
  14. package/dist/src/diagnostics/source-text.js.map +1 -0
  15. package/dist/src/driver/bundler/graph.js +2 -1
  16. package/dist/src/driver/bundler/graph.js.map +1 -1
  17. package/dist/src/driver/compiler.js +3 -2
  18. package/dist/src/driver/compiler.js.map +1 -1
  19. package/dist/src/lexer/scanner/strings.js +16 -3
  20. package/dist/src/lexer/scanner/strings.js.map +1 -1
  21. package/dist/tests/cli/hints.test.d.ts +9 -0
  22. package/dist/tests/cli/hints.test.js +143 -0
  23. package/dist/tests/cli/hints.test.js.map +1 -0
  24. package/dist/tests/cli/main.test.js +6 -4
  25. package/dist/tests/cli/main.test.js.map +1 -1
  26. package/dist/tests/codegen/llvm.test.js +17 -2
  27. package/dist/tests/codegen/llvm.test.js.map +1 -1
  28. package/dist/tests/helpers.js +3 -2
  29. package/dist/tests/helpers.js.map +1 -1
  30. package/dist/tests/lexer/strings.test.js +14 -2
  31. package/dist/tests/lexer/strings.test.js.map +1 -1
  32. package/doc/DESIGN.md +117 -0
  33. package/doc/ai/README.md +63 -0
  34. package/doc/ai/build-recipe.md +137 -0
  35. package/doc/ai/cli.md +142 -0
  36. package/doc/ai/contributing.md +196 -0
  37. package/doc/ai/extensions.md +148 -0
  38. package/doc/ai/language-support.md +152 -0
  39. package/doc/ai/troubleshooting.md +163 -0
  40. package/doc/ai/zh-CN/README.md +56 -0
  41. package/doc/ai/zh-CN/build-recipe.md +132 -0
  42. package/doc/ai/zh-CN/cli.md +127 -0
  43. package/doc/ai/zh-CN/contributing.md +173 -0
  44. package/doc/ai/zh-CN/extensions.md +139 -0
  45. package/doc/ai/zh-CN/language-support.md +147 -0
  46. package/doc/ai/zh-CN/troubleshooting.md +150 -0
  47. package/doc/gui-scripts.md +350 -0
  48. package/doc/gui.md +646 -0
  49. package/doc/icon.md +265 -0
  50. package/doc/implemented.md +373 -0
  51. package/doc/node-implemented.md +588 -0
  52. package/doc/node-unimplemented.md +167 -0
  53. package/doc/post/announce.md +43 -0
  54. package/doc/requirements.md +145 -0
  55. package/doc/unimplemented.md +286 -0
  56. package/doc/xbintsc.config.schema.json +67 -0
  57. package/doc/zh-CN/DESIGN.md +104 -0
  58. package/doc/zh-CN/gui-scripts.md +329 -0
  59. package/doc/zh-CN/gui.md +588 -0
  60. package/doc/zh-CN/icon.md +241 -0
  61. package/doc/zh-CN/implemented.md +365 -0
  62. package/doc/zh-CN/node-implemented.md +533 -0
  63. package/doc/zh-CN/node-unimplemented.md +141 -0
  64. package/doc/zh-CN/plan-require-node-modules.md +284 -0
  65. package/doc/zh-CN/post/announce.md +47 -0
  66. package/doc/zh-CN/requirements.md +134 -0
  67. package/doc/zh-CN/unimplemented.md +247 -0
  68. package/llms.txt +45 -0
  69. package/package.json +4 -1
  70. package/runtime/ext_gui/gui.cpp +3 -1
  71. package/runtime/ext_gui/renderer.cpp +13 -11
  72. package/runtime/ext_gui/renderer_image.cpp +12 -8
  73. package/runtime/ext_gui/renderer_shaders.h +131 -4
  74. package/runtime/ext_gui/renderer_shaders_data.h +1809 -0
  75. package/runtime/ext_gui/renderer_text.cpp +12 -8
  76. package/runtime/ext_gui/shaders.hlsl +98 -0
  77. package/runtime/ext_gui/spirv/fill.frag +19 -0
  78. package/runtime/ext_gui/spirv/fill.vert +42 -0
  79. package/runtime/ext_gui/spirv/image.frag +16 -0
  80. package/runtime/ext_gui/spirv/quad.vert +30 -0
  81. package/runtime/ext_gui/spirv/text.frag +16 -0
  82. package/scripts/build-gui-shaders.mjs +204 -0
  83. package/scripts/build-gui.ts +35 -0
  84. package/scripts/check-file-length.ts +5 -1
  85. package/src/cli/hints.ts +194 -0
  86. package/src/cli/main.ts +82 -9
  87. package/src/codegen/generator/tables.ts +60 -14
  88. package/src/diagnostics/source-text.ts +78 -0
  89. package/src/driver/bundler/graph.ts +2 -1
  90. package/src/driver/compiler.ts +3 -2
  91. package/src/lexer/scanner/strings.ts +16 -3
package/doc/ai/cli.md ADDED
@@ -0,0 +1,142 @@
1
+ # CLI and configuration reference
2
+
3
+ The authoritative `--help` text is the `HELP` constant in
4
+ [../../src/cli/main.ts](../../src/cli/main.ts). This page adds the semantics that
5
+ the help text leaves implicit.
6
+
7
+ ## Commands
8
+
9
+ | Command | Purpose | Needs clang? |
10
+ | --- | --- | --- |
11
+ | `xbintsc build <file.ts>` | Compile to `exe` (default), `obj` or `ir` | yes, except `--emit ir` |
12
+ | `xbintsc run <file.ts> [-- args]` | Compile to an executable and execute it | yes (`--emit exe` only) |
13
+ | `xbintsc emit <file.ts>` | Print LLVM IR to stdout, write nothing | no |
14
+ | `xbintsc doctor` | Report the resolved toolchain, runtime and icon tools | only for the probe |
15
+ | `xbintsc version` | Print the version | no |
16
+ | `xbintsc help` | Print the help text | no |
17
+
18
+ Any command also accepts `--help`, which prints the help text and exits 0. An
19
+ unknown command prints an error plus the help text and exits 1.
20
+
21
+ ## Options
22
+
23
+ ```
24
+ -o, --output <path> Explicit output path (overrides --out and the config)
25
+ --out <dir> Output directory (default: build/)
26
+ --emit <kind> exe | obj | ir (default: exe)
27
+ -O0 .. -O3 Optimization level passed to clang (default: -O2)
28
+ --ext <names> Enable bundled extensions, comma separated (e.g. node,gui)
29
+ --ext-native <m> Register a C++/Rust extension from a JSON manifest
30
+ (comma separated for several)
31
+ --config <path> Use this project config instead of discovering one
32
+ --no-config Do not read any project config
33
+ --icon <path> Embed an application icon (PNG/ICO/ICNS)
34
+ --bundle macOS: also produce a <name>.app bundle
35
+ --app-name <name> Bundle / display name
36
+ --app-id <id> macOS bundle identifier (e.g. com.example.demo)
37
+ --force Ignore the incremental cache
38
+ --verbose Print progress information
39
+ ```
40
+
41
+ Parsing details that occasionally surprise:
42
+
43
+ - `--flag=value` and `--flag value` are both accepted.
44
+ - `-O`, `-O1`, `-O2`, `-O3` all work; a bare `-O` means `-O2`.
45
+ - `--` ends option parsing: everything after it belongs to the program under
46
+ `run`, or is a positional for the other commands.
47
+ - `-o` consumes the next argument unconditionally; the other value flags leave
48
+ the value unset when the next argument starts with `-`.
49
+
50
+ ## Project config
51
+
52
+ `xbintsc.config.json` is discovered by walking **up** from the entry file's
53
+ directory (or from the current directory when no entry was given). Every path in
54
+ the file resolves relative to the config file, and any CLI flag overrides the
55
+ matching field. `--no-config` skips discovery entirely.
56
+
57
+ | Field | Type | Meaning |
58
+ | --- | --- | --- |
59
+ | `entry` | string | Entry TypeScript file, so `xbintsc build` needs no positional |
60
+ | `outDir` | string | Output directory (default `build/`) |
61
+ | `output` | string | Explicit output path, overriding `outDir` |
62
+ | `optimize` | `"0"|"1"|"2"|"3"` | Optimization level |
63
+ | `extensions` | string[] | Bundled extensions to enable, e.g. `["node"]` |
64
+ | `extNative` | string[] | Native extension manifest paths |
65
+ | `force` | boolean | Ignore the incremental cache |
66
+ | `app.name` / `app.icon` / `app.bundle` / `app.bundleId` | | Application metadata ([icon.md](../icon.md)) |
67
+
68
+ JSON schema: [xbintsc.config.schema.json](../xbintsc.config.schema.json). An
69
+ unreadable or malformed config fails the build with `invalid JSON` or
70
+ `Unable to read` rather than silently falling back to defaults.
71
+
72
+ ## Environment variables
73
+
74
+ | Variable | Effect |
75
+ | --- | --- |
76
+ | `xbintsc_CLANG` | Use this clang instead of resolving one from `PATH` |
77
+ | `xbintsc_LINKER_ARGS` | Extra linker flags (e.g. `-fuse-ld=lld`) |
78
+ | `xbintsc_CACHE_DIR` | Object-cache directory (default: `.xbintsc`) |
79
+ | `xbintsc_PREFER_PREBUILT` | `0` forces the runtime to be compiled from source |
80
+ | `xbintsc_BINARY` | Native compiler binary the `bin/` launcher should run |
81
+
82
+ ## Incremental compilation
83
+
84
+ Each build is keyed on the entry source hash, the compiler version, the build
85
+ options, the platform and the active extension set
86
+ ([../../src/driver/cache.ts](../../src/driver/cache.ts)). If every recorded
87
+ output still exists, the build returns immediately and prints `(cached)`. The C
88
+ runtime and extension sources are cached the same way; objects live in
89
+ `.xbintsc/` (or `xbintsc_CACHE_DIR`).
90
+
91
+ `--force` (or `force: true`) bypasses the freshness check. If a build ever
92
+ returns stale output, that key is the first thing to inspect — and note that the
93
+ object cache key deliberately excludes compiler flags, which is why the runtime
94
+ coverage script uses a separate cache directory.
95
+
96
+ ## Exit codes
97
+
98
+ - `0` — success (for `run`: the program exited 0).
99
+ - `1` — diagnostics, a usage error, or a failure in the compiler itself.
100
+ - `run` otherwise propagates the program's own exit status.
101
+
102
+ ## Diagnostics
103
+
104
+ Errors are rendered as `file:line:col - error TS<code>: <message>` with a source
105
+ excerpt and caret underline. Codes are grouped by stage
106
+ ([../../src/diagnostics/diagnostic.ts](../../src/diagnostics/diagnostic.ts)):
107
+
108
+ | Range | Stage |
109
+ | --- | --- |
110
+ | TS1xxx | lexer |
111
+ | TS2xxx | parser |
112
+ | TS3xxx | binder |
113
+ | TS4xxx | checker (`TS4005` is the `UnsupportedFeature` code) |
114
+ | TS5xxx | codegen |
115
+ | TS6xxx | driver: `TS6001` module not found, `TS6002` IO, `TS6003` toolchain, `TS6004` cache |
116
+
117
+ The CLI appends a `hint:` line pointing at the relevant document (for example
118
+ [language-support.md](./language-support.md) for `TS4005`, or
119
+ [troubleshooting.md](./troubleshooting.md) for `TS6001`/`TS6003`). Toolchain and
120
+ IO failures throw instead of producing a diagnostic, so the CLI reports those as
121
+ `xbintsc: <message>` plus the same style of hint.
122
+
123
+ ## Programmatic API
124
+
125
+ ```ts
126
+ import { build, compileString } from "xbintsc";
127
+ ```
128
+
129
+ - `compileString(source, fileName?, extensions?)` → `{ ir, diagnostics }`.
130
+ Pure IR generation: no filesystem, no clang.
131
+ - `build(entryPath, options?)` → `BuildResult`. `options` accepts `output`,
132
+ `outDir`, `emit`, `optimize`, `force`, `verbose`, `extensions` (a registry),
133
+ `app`, `clang`, `preferPrebuilt`.
134
+ - `canonicalize` of results: `{ outputPath, irPath?, cached, diagnostics, ir?,
135
+ bundlePath? }`.
136
+
137
+ `build` reports source-level problems through `diagnostics` — always check
138
+ `diagnostics.some((d) => d.category === "error")` before using the artifact — but
139
+ **throws** `ToolchainError` when clang itself fails
140
+ ([../../src/driver/toolchain.ts](../../src/driver/toolchain.ts)). The
141
+ `xbintsc/driver` subpath additionally exports the cache, toolchain resolution,
142
+ config loader, icon and macOS-bundle helpers.
@@ -0,0 +1,196 @@
1
+ # Contributing to the compiler
2
+
3
+ Use this page after [../AGENTS.md](../../AGENTS.md), which covers the layout, the
4
+ build gate and the rules. This one covers the workflow details that are easy to
5
+ get wrong.
6
+
7
+ ## Set up
8
+
9
+ ```bash
10
+ npm install
11
+ npm run typecheck # tsc --noEmit
12
+ npm run lint # eslint + the 600-line file budget
13
+ npm test # unit + end-to-end
14
+ ```
15
+
16
+ Node.js ≥ 22 is required to run the compiler from source, and a **clang 16+**
17
+ toolchain is required for anything that links a binary (see
18
+ [troubleshooting.md](./troubleshooting.md)). `xbintsc emit` needs neither clang
19
+ nor the runtime library, which makes it the fastest inner loop.
20
+
21
+ Optional but recommended — install the repository hook:
22
+
23
+ ```bash
24
+ git config core.hooksPath .githooks
25
+ ```
26
+
27
+ `.githooks/pre-commit` **bumps the patch version on every commit** and folds
28
+ `package.json` + `package-lock.json` into that commit (`npm version patch
29
+ --no-git-tag-version`). Expect a version bump in your commits; do not countermand
30
+ it. Release tags are created by `release.yml`, never by the hook.
31
+
32
+ ## The test suites
33
+
34
+ | Command | Scope |
35
+ | --- | --- |
36
+ | `npm test` | everything: per-module unit tests plus `tests/e2e` |
37
+ | `npm run test:e2e` | only the compile-and-run tests |
38
+ | `npm run test:watch` | vitest in watch mode |
39
+ | `npm run coverage` | V8 coverage of the TypeScript compiler (`src/`) |
40
+ | `npm run coverage:runtime` | LLVM coverage of the C runtime (`runtime/`) |
41
+
42
+ Unit tests live one directory per module under `tests/` (`lexer`, `parser`,
43
+ `binder`, `codegen`, `driver`, `extensions`, `cli`, `e2e`) and mirror the source
44
+ tree. Edge-case tests belong next to the module they cover.
45
+
46
+ `tests/e2e/` compiles real programs and runs the resulting binaries, including a
47
+ **differential harness** (`tests/e2e/differential-*.test.ts`) that executes the
48
+ same source through both xbintsc and Node and compares output byte-for-byte. That
49
+ harness is the strongest tool in the repository: when you touch semantics, add a
50
+ case there rather than asserting a hand-written expected string. Tests that need
51
+ clang skip themselves when it is unavailable (`hasClang()` in
52
+ [tests/helpers.ts](../../tests/helpers.ts)) — do not turn that into a silent
53
+ pass.
54
+
55
+ ## Two invariants you must not break
56
+
57
+ ### 1. The self-hosting fixpoint
58
+
59
+ CI (`self-host` job in [.github/workflows/ci.yml](../../.github/workflows/ci.yml))
60
+ compiles the compiler with itself and requires the emitted IR to be identical
61
+ across generations:
62
+
63
+ ```
64
+ source --emit--> ref.ll
65
+ source --build--> gen1 binary
66
+ gen1 --emit--> gen2.ll # must equal ref.ll
67
+ gen1 --build--> gen2 binary
68
+ gen2 --emit--> gen3.ll # must equal ref.ll
69
+ ```
70
+
71
+ By consequence: **any change that alters emitted IR must still be reproducible by
72
+ the compiler being changed**, and the IR must be byte-for-byte deterministic —
73
+ iteration order, generated symbol names and numbering included. Never introduce
74
+ nondeterminism (a `Map`/`Set` iteration over insertion-ordered data you did not
75
+ control, a timestamp, a filesystem-order dependency) into codegen.
76
+
77
+ A second consequence is the string model: the compiler's own source text has to
78
+ mean the same thing in every generation. A compiled string is UTF-8 bytes, so the
79
+ self-hosted `readFileSync` (`--ext node`) hands the scanner the bytes of a file,
80
+ while Node hands it the same file already decoded — read source files through
81
+ `decodeUtf8` ([src/diagnostics/utf8.ts](../../src/diagnostics/utf8.ts)) and never
82
+ assume a code unit is a byte or a character. Getting this wrong shows up as a
83
+ non-ASCII literal (`—` in a hint string) coming out re-encoded in `gen2.ll`, which
84
+ is why the emitted IR is compared byte-for-byte rather than line-by-line.
85
+
86
+ Verify locally:
87
+
88
+ ```bash
89
+ npx tsx src/cli/main.ts emit src/cli/main.ts --ext node > scratch/ref.ll
90
+ npx tsx src/cli/main.ts build src/cli/main.ts --ext node --out scratch/self --force
91
+ ./scratch/self/main emit src/cli/main.ts --ext node > scratch/gen2.ll # .exe on Windows
92
+ diff scratch/ref.ll scratch/gen2.ll
93
+ ```
94
+
95
+ ### 2. The runtime ABI and value model
96
+
97
+ - Every compiled function uses
98
+ `xt_value fn(xt_value env, int32_t argc, xt_value *argv)` — direct calls and
99
+ closure calls share this one path, and `env` threads captured variables
100
+ through boxes.
101
+ - Every value is a single 64-bit word: doubles unboxed, everything else a tagged
102
+ pointer (16-bit tag + 48-bit payload). The representation is defined **once**
103
+ in [../../src/codegen/values.ts](../../src/codegen/values.ts) and
104
+ [../../runtime/rt.h](../../runtime/rt.h); change both together or not at all.
105
+ - Awkward JS semantics (`+` coercion, relational comparison, property access,
106
+ inspection) are delegated to `@xt_*` runtime calls rather than inlined.
107
+ - The GC is a non-moving mark-sweep collector: new heap objects go through
108
+ `xt_alloc`, and a value must be reachable from an explicit root slot, a
109
+ registered root provider (the event loop, the microtask queue) or the
110
+ conservative C-stack scan while it is live. A value held only in a C local that
111
+ is not on the scanned stack is a bug.
112
+
113
+ ## Changing the runtime
114
+
115
+ `runtime/` is C, split by function across translation units (`xt_alloc.c`,
116
+ `xt_values.c`, `xt_containers.c`, `xt_stdlib.c`, …) sharing
117
+ `runtime/rt_internal.h`. The runtime must be rebuilt for C changes to take
118
+ effect:
119
+
120
+ ```bash
121
+ npm run runtime # tsx scripts/build-runtime.ts -> runtime/lib/<os>-<arch>/
122
+ npm run runtime:clean # drop build/runtime-obj and runtime/lib
123
+ ```
124
+
125
+ Add a new C file only together with the place that lists the runtime sources
126
+ (`RUNTIME_SOURCES` in [../../src/driver/compiler.ts](../../src/driver/compiler.ts)),
127
+ because that list decides what gets compiled and linked. Extension C sources are
128
+ declared by the extension itself.
129
+
130
+ ## Adding or extending an extension
131
+
132
+ An extension is a plain object ([../../src/extensions/registry.ts](../../src/extensions/registry.ts))
133
+ contributing `runtimeSources()`, `modules()` / `builtins()`, and optionally
134
+ `nativeObjects()` or `assetLoaders()`. The Node extension is one folder per
135
+ module, pairing its TypeScript exports with the C sources that implement them:
136
+
137
+ ```
138
+ src/extensions/node/fs/index.ts runtime/ext_node/fs/read_file.c
139
+ ```
140
+
141
+ Adding a Node module therefore means dropping a folder in each place and
142
+ registering it in the module list; the core compiler never changes. If your
143
+ feature is optional or platform-specific, it belongs in an extension rather than
144
+ in `src/codegen`. Bundled extensions are listed in
145
+ [../../src/extensions/catalog.ts](../../src/extensions/catalog.ts) — an extension
146
+ listed there but not enabled produces the actionable `pass --ext <name>` hint, so
147
+ add modules to the list even when the feature is off by default.
148
+
149
+ Extension authoring from C++/Rust without touching the compiler is documented in
150
+ [../../examples/extensions/README.md](../../examples/extensions/README.md).
151
+
152
+ ## Adding a diagnostic
153
+
154
+ 1. Add a stable code to the enum grouped by pipeline stage in
155
+ [../../src/diagnostics/diagnostic.ts](../../src/diagnostics/diagnostic.ts)
156
+ (1xxx lexer, 2xxx parser, 3xxx binder, 4xxx checker, 5xxx codegen, 6xxx
157
+ driver). Codes are a public interface — never renumber one.
158
+ 2. Emit it with the most specific range you have; `formatDiagnostic` renders the
159
+ file, line, column, excerpt and caret from it.
160
+ 3. Make the message actionable, in the style of the existing ones: say what is
161
+ wrong *and* what to do (`pass --ext node`, `use an ESM import`, …). If a
162
+ reader needs more, the CLI hint layer maps codes to a document — extend that
163
+ map in [../../src/cli/main.ts](../../src/cli/main.ts) when you add a code.
164
+ 4. Cover it in `tests/diagnostics` or the module's own test.
165
+
166
+ ## Documentation is part of the change
167
+
168
+ A feature is not done until the docs match:
169
+
170
+ | Document | Update when |
171
+ | --- | --- |
172
+ | [../implemented.md](../implemented.md) | a feature starts working |
173
+ | [../unimplemented.md](../unimplemented.md) | a limitation appears or is lifted |
174
+ | [../node-implemented.md](../node-implemented.md) / [../node-unimplemented.md](../node-unimplemented.md) | Node module coverage changes |
175
+ | [language-support.md](./language-support.md) | the AI-facing summary of the above changes |
176
+ | [../requirements.md](../requirements.md) | the toolchain or platform requirements change |
177
+ | [cli.md](./cli.md) | a flag, config field or environment variable changes |
178
+
179
+ Keep the Chinese translation (`doc/zh-CN/`, `doc/ai/zh-CN/`) in step with the
180
+ English source; a half-translated page is worse than none because it silently
181
+ goes stale.
182
+
183
+ ## Repository hygiene
184
+
185
+ - **600 lines per code file**, enforced by ESLint (`max-lines`) for TS/JS and by
186
+ [scripts/check-file-length.ts](../../scripts/check-file-length.ts) for C, C++,
187
+ Rust, `.inc` and shell files. Split by responsibility instead of growing a
188
+ file; vendored sources are exempt.
189
+ - **No `any`**; `prefer-const` and `eqeqeq` are errors. The
190
+ declaration-merging pattern in the parser/generator is deliberately allowed.
191
+ - **Public API changes** go through [../../src/index.ts](../../src/index.ts) and
192
+ must keep the `xbintsc` / `xbintsc/driver` export maps in
193
+ [../../package.json](../../package.json) valid.
194
+ - `npm run package-release` assembles the per-platform archives into
195
+ `dist/release/`; the release workflow owns tagging, so never push a tag by
196
+ hand.
@@ -0,0 +1,148 @@
1
+ # Extensions: Node modules, GUI, C++/Rust libraries
2
+
3
+ The compiler core is platform-agnostic. Anything platform-specific — Node's
4
+ `fs`, an HTML/CSS renderer, your own C++ library — arrives as an **extension**
5
+ that contributes module bindings plus the C/C++ sources or objects to link.
6
+
7
+ ## The rule that trips people up
8
+
9
+ **An import of an extension-provided module fails until the extension is
10
+ enabled.** xbintsc knows the module exists and says exactly what to pass:
11
+
12
+ ```bash
13
+ $ xbintsc run app.ts # app.ts: import { readFileSync } from "fs";
14
+ error TS6001: module 'fs' is provided by the 'node' extension; pass --ext node
15
+ ```
16
+
17
+ ```bash
18
+ xbintsc run app.ts --ext node
19
+ ```
20
+
21
+ Enable several at once with a comma-separated list: `--ext node,gui`. In a
22
+ project, put them in `xbintsc.config.json` so no flag is needed:
23
+
24
+ ```json
25
+ { "entry": "src/app.ts", "outDir": "build", "extensions": ["node"] }
26
+ ```
27
+
28
+ ## Bundled extensions
29
+
30
+ ### `node` — Node built-in modules
31
+
32
+ Enable with `--ext node`. A module is importable by its bare name or the
33
+ `node:` prefix (`import { readFileSync } from "node:fs"`), and both spellings
34
+ resolve to the same implementation.
35
+
36
+ | Module | Notes |
37
+ | --- | --- |
38
+ | `fs` | **synchronous API only** (`readFileSync`, `writeFileSync`, …); `fs/promises` is separate |
39
+ | `fs/promises` | promise-based file APIs |
40
+ | `path` | also hooks namespace dispatch, so `path.join(...)` works |
41
+ | `os` | |
42
+ | `process` | `process.cwd()`, `argv`, `env`, …; namespace dispatch too |
43
+ | `buffer` | |
44
+ | `crypto` | |
45
+ | `stream`, `stream/promises` | |
46
+ | `events` | |
47
+ | `net`, `dgram`, `http` | sockets and servers on the xbintsc event loop (see the async caveat below) |
48
+ | `child_process` | |
49
+ | `worker_threads` | |
50
+ | `util`, `querystring`, `url`, `assert`, `test`, `zlib` | |
51
+
52
+ Coverage per module (exact functions and options) is in
53
+ [../node-implemented.md](../node-implemented.md); what is missing is in
54
+ [../node-unimplemented.md](../node-unimplemented.md).
55
+
56
+ Remember the runtime model before building on `net`/`http`: the event loop runs
57
+ **after** the program body, and `async`/`await` is a synchronous microtask
58
+ model. See [language-support.md](./language-support.md).
59
+
60
+ ### `gui` — HTML/CSS window
61
+
62
+ Enable with `--ext gui` and import the `gui` module:
63
+
64
+ ```ts
65
+ import { createWindow, run } from "gui";
66
+ ```
67
+
68
+ It is a self-contained GPU-accelerated renderer (own HTML parser, CSS cascade,
69
+ layout and compositor) — not a system WebView. It ships as a per-platform
70
+ prebuilt `gui.a`/`gui.lib` because it is C++. If that archive is missing for
71
+ your platform, the build fails with an actionable message; design and roadmap
72
+ live in [../gui.md](../gui.md), and the scripting surface in
73
+ [../gui-scripts.md](../gui-scripts.md).
74
+
75
+ ## Third-party npm packages
76
+
77
+ Bare specifiers that no enabled extension claims are looked up in `node_modules`
78
+ and bundled **as source**. A package that is not plain ESM TypeScript/JavaScript
79
+ — or that relies on CommonJS, `require`, `__dirname`, or circular dependencies —
80
+ will not work. `require()` is rejected with a hint to convert to `import`.
81
+
82
+ Practical consequence: for a Node program, enabling `--ext node` is usually the
83
+ right answer; reach for an npm dependency only when you must.
84
+
85
+ ## Native extensions (C++ / Rust, no compiler changes)
86
+
87
+ Any code that exposes `extern "C"` entry points with the runtime ABI can be
88
+ linked in:
89
+
90
+ ```c
91
+ xt_value my_fn(int32_t argc, xt_value *argv);
92
+ ```
93
+
94
+ Build it with an **external** toolchain (clang++ or cargo), describe the
95
+ artifacts in a JSON manifest, and pass the manifest:
96
+
97
+ ```bash
98
+ xbintsc build demo.ts --ext-native ./xbintsc.manifest.json
99
+ xbintsc build demo.ts --ext-native a.json,b.json # several
100
+ ```
101
+
102
+ ```jsonc
103
+ {
104
+ "name": "mathx-cpp", // required, unique
105
+ "objects": ["build/mathx.o"], // .o / .a / .lib, relative to this file
106
+ "linkerFlagsByPlatform": { // C++/Rust runtimes
107
+ "linux": ["-lstdc++", "-lm"],
108
+ "darwin": ["-lc++"],
109
+ "win32": ["-lmsvcprt"]
110
+ },
111
+ "builtins": { "cppClamp": { "symbol": "mathx_clamp" } }, // no import needed
112
+ "modules": {
113
+ "mathx": { "exports": { "add": { "symbol": "mathx_add" } } }
114
+ }
115
+ }
116
+ ```
117
+
118
+ `import { add } from "mathx"` then lowers to the native symbol exactly like a C
119
+ runtime binding; `builtins` are globally callable without importing.
120
+
121
+ Authoring helpers: `runtime/xt_ext.h` (C/C++) and `runtime/xt_ext.rs` (Rust) —
122
+ they wrap the argument and value helpers. Key ABI facts: values are 64-bit
123
+ NaN-boxed words; strings are **not** NUL-terminated, so pair `xt_string_data`
124
+ with `xt_string_length_value`; the runtime never moves or frees memory it handed
125
+ out. Build with the same clang/ABI that xbintsc resolves (`xbintsc doctor`
126
+ prints it), and on Windows build inside an **x64/ARM64 Native Tools Command
127
+ Prompt**.
128
+
129
+ Working projects to copy: [`examples/extensions/cpp`](../../examples/extensions/cpp)
130
+ and [`examples/extensions/rust`](../../examples/extensions/rust); the full guide
131
+ is [../../examples/extensions/README.md](../../examples/extensions/README.md).
132
+
133
+ ## Programmatic registration
134
+
135
+ ```ts
136
+ import { build, createDefaultRegistry, nativeExtensionFromManifest, nodeExtension } from "xbintsc";
137
+
138
+ const extensions = createDefaultRegistry()
139
+ .register(nodeExtension)
140
+ .register(nativeExtensionFromManifest("./xbintsc.manifest.json"));
141
+
142
+ build("demo.ts", { extensions });
143
+ ```
144
+
145
+ The registry is also how a host tool hints at a disabled extension, which is what
146
+ produces the "pass `--ext node`" message instead of a confusing downstream error
147
+ ([../../src/extensions/catalog.ts](../../src/extensions/catalog.ts)). The list of
148
+ bundled extensions is `bundledExtensions()`.
@@ -0,0 +1,152 @@
1
+ # Language support: what compiles, what behaves differently
2
+
3
+ xbintsc compiles a **practical subset** of TypeScript. Use this page to decide
4
+ what you can write; follow the links for exact wording.
5
+
6
+ Canonical detail (always current, much longer):
7
+
8
+ - [../implemented.md](../implemented.md) — everything that works, by compiler stage
9
+ - [../unimplemented.md](../unimplemented.md) — unsupported syntax, deviations, quick reference
10
+ - [../node-implemented.md](../node-implemented.md) / [../node-unimplemented.md](../node-unimplemented.md) — the Node extension
11
+
12
+ > Chinese: [../zh-CN/implemented.md](../zh-CN/implemented.md), [../zh-CN/unimplemented.md](../zh-CN/unimplemented.md)
13
+
14
+ ## Legend
15
+
16
+ | Mark | Meaning |
17
+ | --- | --- |
18
+ | ✅ | Works as in Node/TypeScript |
19
+ | ⚠️ | Works but deviates from the standard |
20
+ | 🚫 | Rejected — parse error or `UnsupportedFeature` |
21
+ | — | Parsed and erased, with no runtime effect |
22
+
23
+ ## Statements and declarations
24
+
25
+ | Feature | Status | Notes |
26
+ | --- | --- | --- |
27
+ | `var` / `let` / `const`, blocks, `if`, `for`, `for...of`, `for...in`, `while`, `do...while` | ✅ | `for...in` enumerates own keys only (no prototype chain) |
28
+ | `switch`, `break`, `continue`, labels | ✅ | labeled `break`/`continue` included |
29
+ | `try` / `catch` / `finally`, `throw` | ✅ | `finally` runs on early `return`/`break`/`continue` |
30
+ | `function`, arrow functions, default/rest parameters | ✅ | |
31
+ | `return`, `throw` as expressions' operands | ✅ | |
32
+ | `namespace` / `module` declaration | 🚫 | parse ✓, codegen ✗ |
33
+ | `with`, `debugger` | 🚫 | not supported |
34
+ | Top-level `await` | 🚫 | wrap it in an `async` function |
35
+
36
+ ## Expressions and operators
37
+
38
+ | Feature | Status | Notes |
39
+ | --- | --- | --- |
40
+ | Arithmetic, bitwise, logical, comparison, assignment operators | ✅ | ES `ToPrimitive` / `+` coercion match Node |
41
+ | `===` / `!==` / `==` / `!=` | ✅ | loose equality matches Node |
42
+ | Optional chaining `?.`, nullish `??`, logical assignment `??=` | ✅ | whole-chain short-circuit implemented |
43
+ | Template literals, tagged templates | ✅ | tagged templates have `raw` + `String.raw` |
44
+ | Spread / rest in calls, arrays and object literals | ✅ | |
45
+ | Destructuring (bindings, parameters, nested) | ✅ | defaults + rest supported |
46
+ | `delete`, `in`, `instanceof`, `typeof` | ✅ | |
47
+ | Comma operator, `void` | ✅ | |
48
+ | `new.target` | 🚫 | not implemented |
49
+ | `import.meta` | 🚫 | parsed but has no value |
50
+ | `super` | ⚠️ | single-level inheritance correct; depth > 1 may be inaccurate |
51
+
52
+ ## Functions, classes, objects
53
+
54
+ | Feature | Status | Notes |
55
+ | --- | --- | --- |
56
+ | Closures (capture by reference through boxes) | ✅ | one ABI for direct and closure calls |
57
+ | `this`, method calls, arrow lexical `this` | ✅ | arrow functions get their own `arguments`, unlike JS |
58
+ | `call` / `apply` / `bind`, `fn.name` / `fn.length` | ✅ | a bound closure does not track partial-argument `length` |
59
+ | First-class built-in methods (`arr.map` as a value) | ✅ | exposed as *unbound* method values; a detached call throws like JS |
60
+ | `fn.toString()` | ⚠️ | returns `function name() { [native code] }`, not the source text |
61
+ | Classes: fields, methods, statics, getters/setters, `extends`/`super`, `instanceof` | ✅ | |
62
+ | Constructor parameter properties `constructor(public x: T)` | ✅ | |
63
+ | `#private` fields / methods / statics | ⚠️ | stored under a literal `#x` key; no access enforcement; parent/child name collisions may alias |
64
+ | `enum` / `const enum` | ✅ | forward + reverse mapping |
65
+ | `private` / `protected` / `public` / `readonly` / `abstract` / `implements` | — | erased, no access control |
66
+ | Generators `function*`, `yield`, `yield*` | ✅ | `async` generators are not supported |
67
+ | `arguments` object | ✅ | implicit; arrows see their own parameters |
68
+ | Argument-count validation | 🚫 | never checked, though `fn.length` reports declared arity |
69
+
70
+ ## Async, Promises, event loop
71
+
72
+ | Feature | Status | Notes |
73
+ | --- | --- | --- |
74
+ | `async` / `await`, `Promise`, `Promise.all` | ⚠️ | **synchronous microtask model** — `await` on a settled promise continues synchronously |
75
+ | `setTimeout` and callbacks | ⚠️ | the event loop runs **after** the program body, so a promise settled from a timer cannot be awaited |
76
+ | Sockets / servers (`http`, `net`, `dgram`) | ⚠️ | same model: callbacks run on the event loop after the program body |
77
+ | A real async event loop, worker threads | 🚫 | not implemented |
78
+
79
+ ## Types: parsed, then erased
80
+
81
+ **No type checking happens.** Type syntax is parsed into the AST and erased
82
+ during binding/codegen:
83
+
84
+ | Construct | Status |
85
+ | --- | --- |
86
+ | Type annotations, return types, type aliases, interfaces | — parsed, erased, never checked |
87
+ | Generic type parameters and constraints | — no runtime instantiation |
88
+ | `as`, `satisfies`, non-null `!` | — erased, no assertion semantics |
89
+ | Optional-chaining type narrowing | 🚫 |
90
+ | Diagnostics `TS4001`–`TS4004` (`TypeMismatch`, `NotCallable`, `PropertyNotFound`, `ArgumentCountMismatch`) | 🚫 defined but never emitted |
91
+
92
+ A type error in your source is therefore not a compile error — only runtime
93
+ behaviour is checked. Write the runtime code you mean.
94
+
95
+ ## Standard library
96
+
97
+ Implemented: `Math`, `JSON`, `Date`, `RegExp`, `Map`, `Set`, `WeakMap`/`WeakSet`,
98
+ `Symbol`, `Error` family, `BigInt`, `Array`/`String`/`Number`/`Object` methods,
99
+ typed arrays, `String.prototype.match`/`split`/`replace` with capture groups,
100
+ immutable array helpers (`toReversed`, `toSorted`, `toSpliced`, `with`), global
101
+ URI functions.
102
+
103
+ | Missing or different | Status |
104
+ | --- | --- |
105
+ | `String.prototype.normalize`, `structuredClone` | 🚫 |
106
+ | `Error.stack` capture | 🚫 (not captured) |
107
+ | `Object.getPrototypeOf({})` | ⚠️ returns `undefined`, not `Object.prototype` |
108
+ | Global RegExp `lastIndex` | ⚠️ `test`/`exec` ignore it for `/g` and `/y` |
109
+ | Sparse array holes, array out-of-bounds | ⚠️ holes are not tracked distinctly; out-of-bounds reads give `undefined` |
110
+
111
+ See section 3 of [../unimplemented.md](../unimplemented.md) for the full list.
112
+
113
+ ## Modules
114
+
115
+ | Feature | Status | Notes |
116
+ | --- | --- | --- |
117
+ | ESM `import` / `export` | ✅ | |
118
+ | Relative multi-file bundling | ✅ | a `./helper.js` specifier resolves to `helper.ts` |
119
+ | `import * as ns`, default and named imports | ✅ | |
120
+ | Node built-in modules | ✅ | only with `--ext node` — see [extensions.md](./extensions.md) |
121
+ | Bare third-party npm packages | 🚫 | `node_modules` ESM packages are bundled as source only when no extension claims the specifier |
122
+ | `require()` / CommonJS | 🚫 | rejected with a hint to use `import` |
123
+ | Circular dependencies | 🚫 | |
124
+ | Live bindings | ⚠️ | imports and namespace members are snapshots |
125
+ | `import.meta`, `__dirname`, `__filename` | 🚫 | |
126
+
127
+ ## Strings, numbers, memory
128
+
129
+ | Item | Behaviour |
130
+ | --- | --- |
131
+ | `String.prototype.length` | ⚠️ **UTF-8 bytes**, not UTF-16 units: `"é".length === 1`, `"😀".length === 4`; `codePointAt` differs accordingly |
132
+ | Number formatting and coercion | ✅ matches Node (`toFixed`, `toPrecision`, hex/octal/binary parsing) |
133
+ | `BigInt` | ✅ implemented |
134
+ | Garbage collection | ✅ non-moving mark-sweep; explicit roots plus a conservative C-stack scan; single-threaded and stop-the-world; no weak references |
135
+ | Threads | 🚫 single-threaded; `worker_threads` exists in the Node extension only |
136
+
137
+ ## Platform
138
+
139
+ Builds target macOS, Linux and Windows on x64 and arm64. On Windows the target
140
+ is the **MSVC ABI**, so clang needs the MSVC/SDK environment — see
141
+ [../requirements.md](../requirements.md). clang **16 or newer** is required.
142
+
143
+ ## Before you write a large program
144
+
145
+ 1. Skim the table for the features you plan to use; open
146
+ [../unimplemented.md](../unimplemented.md) for anything marked ⚠️ or 🚫.
147
+ 2. Prototype with `xbintsc emit app.ts` — the cheapest way to learn whether a
148
+ construct is supported, since it needs no clang.
149
+ 3. Run the real thing with `xbintsc run app.ts`. If a build fails, go to
150
+ [troubleshooting.md](./troubleshooting.md).
151
+ 4. When exact Node behaviour matters, run the same program under Node and
152
+ compare output; the project's own test suite does this differential check.