No runtime, no VM
The output is a native executable from clang -O2, not a bundled interpreter. Zero startup warmup, small binaries, plain libc by default.
You write .ts. It writes LLVM IR, hands it to clang, and out comes a real executable. Your operating system is none the wiser. No runtime. No V8. No Electron.
Single-developer, experimental, AGPL-3.0. Built for CLI tools & Docker microservices.
$ git clone https://github.com/memphisx/KlainMainLang
$ cd KlainMainLang && make build # → ./klainmain
$ ./klainmain app.ts # → native binary
$ ./app
hello, native worldThese are curated feature-area checklists, “does the core case work?”, not external conformance. They mirror the project's own status matrix, caveats and all.
External conformance · full public suites Low on purpose, and shown on purpose. This is a typed subset: most of Test262 is eval-based untyped JS, and Node's suite is dynamic JavaScript against the full platform. Both largely out of scope by design, not silent failures. The honest breakdown.
The output is a native executable from clang -O2, not a bundled interpreter. Zero startup warmup, small binaries, plain libc by default.
http.listen speaks HTTP/1.1 and HTTP/2 (h2c) on one port. fs, worker_threads and cluster give you real OS threads and processes. TLS on both ends.
fetch, URL, WebSocket, Web Crypto, Streams, AbortController, timers, the web APIs that make sense without a DOM, and none of the ones that don't.
number is a JS-faithful 64-bit float by default, opt into sized machine ints (int8…uint64) with a JSDoc width when you want them. Memory is manual by default (never frees, perfect for short-lived CLIs); pass -mm=gc for a real Boehm collector. Crypto / bigint / regex backends chosen per compile.
Every snippet below is an actual file under examples/. It compiles and runs, verified on every build. No slideware.
import http from 'http'
interface Res {
status: number
body: string
headers: Map<string, string>
}
http.listen(8080, (req: HttpRequest): Res => {
let respHeaders: Map<string, string> = new Map<string, string>()
respHeaders.set('Content-Type', 'text/plain')
if (req.path === '/hello') {
let name: string = req.query.has('name') ? req.query.get('name') : 'stranger'
return { status: 200, body: 'hello, ' + name, headers: respHeaders }
}
return { status: 404, body: 'not found: ' + req.path, headers: respHeaders }
})Source text becomes a flat token stream.
Recursive descent with Pratt precedence climbing builds the AST.
Transitive imports are parsed and merged into one AST.
~60 small domain files write LLVM IR text.
The real backend turns IR into a host-arch binary.
→ one .ll, one clang call, one generated main(), one binary that runs unsupervised.
A bare : number is a JS-faithful 64-bit float. Reach for a JSDoc int8…uint64 width when you mean real integers. Memory is manual by default (-mm=gc for a collector). Concurrency is cooperative. Nothing is dynamic. All deliberate.
Not German. Not a clothing brand. Klain Main isn't “klein Main” (a cute little main()), and it isn't the Augsburg streetwear label. It's Greek.
Κλάιν Μάιν is slang for “I don't care”, the good kind: do the thing anyway, even when everyone tells you it's futile, for no better reason than because you can. That's the whole reason this compiler exists, because “how would I even build one” turned out to be a far better rabbit hole than whatever the day's actual plan was.
Read the docs