.nim / .aowl → aowlparse → aowlsem* → aowlhexer* → { aowlc → C · aowljs → JS · aowli → interpret }
A rewrite of the entire Nimony ecosystem
- parser, semantic checker, lowering
- interpreter and runtime
- standard library
- LSP, MCP, vscode, claude plugin
- code fix suggestions, formatter, obfuscator
- and much, much, more...
translates to:
- C
- faithful and native JavaScript & TypeScript
- Python
— all self-hosted, written in itself
Between the frontend stages we use AIF, which is NIF, byte for byte, so any part you find here is compatabile with Nim or Nimony.
The big projects are private for now, but the docs are public and anything private is yours if you just ask — message me on Discord (timbuktu_guy) and I'll add you, no hoops. The playground moves onto the new sem + hexing shortly.
| Project | Docs |
|---|---|
aowl toolchain — aowlparse · aowlsem · aowlhexer · aowlc · aowljs · aowli |
AIF ≡ NIF |
aowlup — rustup for the stack: installs / versions / selects the toolchain (variants · profiles) |
repo ↗ |
aowlabi — the stack's shared value-representation / ABI: one canonical per-type layout + marshal matrix, read by aowlc · aowljs · aowli instead of each keeping its own copy |
docs · repo ↗ |
aowlcode — Claude Code plugin + MCP server (trace/debug, /land, cheap-applier fan-out) |
docs |
| aowllsp — Language Server + VSCode extension | docs |
aowli-release — public, binary-only distribution of aowli (the aowli source is private); runs a nimony program's typed NIF; prebuilt aowli-interp + aowli-dbg, GitHub Release v0.3.1, hardened (obfnif IR + licence gate + stripped), SHA256 + VirusTotal per binary |
docs |
net stack — tcp·net·tls·http·compress·serve·ws·requests — TLS 1.3, HTTP/1.1 · 2 · 3, QUIC + WebTransport, Autobahn WebSocket, single-thread async reactor |
docs |
| web / html / css — typed HTML5 + MDN CSS engine + DSL | docs |
| aowljs / aowlts / aowlpy / aowlhl — idiomatic JS/TS/PY backends + shared HL-IR | ts · py · hl |
· and more, here
The playground grew up into a real in-browser IDE. What was a single-file scratchpad is now a full multi-file workspace with a VS Code-style feel — still running the whole toolchain (parser, semantic checker, interpreter, and the debugger) compiled to JavaScript, entirely in your browser tab.
- Multi-file projects + a file explorer. A dockable explorer tree with right-click context menus (rename / move / delete), multi-select, drag-to-move between folders, preview tabs (single-click opens an italic preview, double-click or an edit keeps it), and a navigation history you drive with the mouse back/forward buttons (or Alt+←/→) — back out of a definition and forward into it, exactly like an editor.
- Clone a repo — or share a whole workspace — from a link. Type
owner/repo(e.g.aoughwl/aowllib) to clone a public GitHub repo client-side, or hand someone a#clone=owner/repolink that spawns them straight into it. The Share button now packs your entire workspace — every project and file — into a single compressed link, not just the active buffer. - The aowli debugger, live in the browser. Run a program and step through it on a flame / depth timeline: every statement is a cell, call depth stacks into lanes, each routine gets its own colour, with a zoomable slice and a full-run minimap — scrub, reverse-step, and jump anywhere. It auto-captures the moment you open it. (Fixing the current-line highlight also traced a neat root cause:
echois a template, so its expansion carries the stdlib's line info, not your call site — the debugger is now file-aware so it never jumps to a foreign line.) - Split editors + browse the standard library. Drag a tab to any edge to open two files side-by-side or stacked. Ctrl-click or F12 on an
importopens the real std source (system,syncio, …) to read inline. And.json/.js/.c/.niffiles now get native highlighting — including a proper NIF grammar — and skip the nimony pipeline entirely, so only.nim/.aowlare type-checked and run. - Latest bundles. The obfuscated aowlsem (the experimental aowl-semantics checker you can switch to) and the aowli interpreter + debugger bundles are refreshed to their newest builds.
Every doc page for a runnable library now carries a "▶ Try it live in the Playground" link, too.
aowlsem spent the rest of the day on the half of a semantic checker that never shows up in its output: deciding which programs are wrong. Yesterday's grind was byte-for-byte agreement on the typed output for valid programs. That says nothing about invalid ones — a checker that quietly accepts a broken program is worse than one that emits a slightly different tree for a good one.
The method is a small tool, now checked in beside the other gates: write ten little programs around one theme — arguments, literals, control flow, generics and closures, declarations, numeric types, exceptions — run each through both aowlsem and the reference compiler, and print the accept/reject verdicts side by side. Every disagreement is the work list. A program aowlsem rejects that the reference accepts is the urgent kind: aowlsem faulting valid code. A program it accepts that the reference rejects is a missing check. Occasionally the disagreement is the third kind — the reference is wrong.
About thirty checks landed this way. A sample of what a user now gets an error for instead of silence: passing a named argument no parameter answers to (with a did-you-mean); for a, b in 0 ..< 3, where the range yields one value per step and there is nothing for b to bind; assigning to the result of a call, which is a value and not a place to store into; a case on a float; the branches of an if-expression disagreeing on their type; a variant object's constructor setting a field from a branch the discriminator did not select; a nested proc reading its enclosing routine's local without being marked a closure; arithmetic on types that have none (true * false, 'z' - 'a', "a" + "a"); indexing a string or seq with something that is not a number; mixing signed and unsigned integers, which nothing implicitly converts between; a set over a non-ordinal element type; deriving from a plain object that was never made a base type; the wrong number of type arguments to a generic; a converter that does not take exactly one parameter; a pragma that is not a real pragma (checked against the whole vocabulary, plus any you declared yourself); and — an entire family that had been silently accepted — an undeclared type name in any position at all: a field, a parameter, a local, an object's parent, a generic argument, an except filter.
Four false positives came out of the same loop, and those mattered more than the gaps. Two methods along one inheritance chain (Animal and Dog) were reported as an ambiguous call — the subtype relation made the two signatures look identical. Shadowing a parameter (proc f(a: int) = let a = a + 1), which is ordinary Nim, was reported as a redeclaration, because parameters share a scope with the body. Both directions of an enum conversion (Color(1), int(green)) were rejected as impossible — enums are ordinal, and both are legal. And a for over an enum range was mis-flagged as iterating something that is not a collection.
That last pair had been hiding: the probe programs also tripped a real error from the reference compiler, so the verdicts "agreed" and the tool said nothing. It now prints why each side rejected, and every new check above was confirmed to fire for the same reason as the reference rather than by coincidence. Re-running the whole day's probes through that lens turned up five more programs rejected for the wrong reason — three fixed today, two written down honestly as still open.
The gates all moved: the byte-exact differential corpus is at 618 modules (five combined-feature programs — a recursively rendered variant tree, a string tokenizer, a two-parameter generic that returns the swapped instance, multi-path result, enum-set algebra — were byte-exact first try and joined it), the accept/reject agreement gate went from 76 to 139, the locked-down error-message snapshots from 64 to 97, and every one of the 71 diagnostic codes has a long-form --explain article behind it. A third case joined the small set where aowlsem is right and the reference compiler is not: proc maxOf[T](a, b: T): T = if a > b: a else: b — textbook Nim that the reference cannot instantiate.
aowli-release is now private indefinitely.
Debugging a big program under aowli stopped meaning "recompile it every time." Pointing the debugger at aowlsem itself took minutes per run — but the interpret is ~1 second; the minutes were the aowlcode debug/trace tools recompiling the whole ~20k-line compiler (plus all of stdlib) from scratch before every run, with -f, then deleting it. We measured the obvious "persistent cache → incremental rebuild" fix, and it doesn't help — a warm no--f rebuild costs the same ~47s (the toolchain has no fast incremental path for this shape yet). The real fix is to skip the compiler entirely when nothing changed: reuse the built .s.nif, recompiling only when a project source is actually edited. The first debug of a session is cold (~47s); every one after is ~1 second. (Or hand the tools a prebuilt .s.nif and skip compiling outright.)
Released aowli v0.3.3 — hybrid-native mode now crosses ref/seq-bearing data. aowli's optional hybrid mode runs the modules you're not debugging as real compiled code while interpreting the one you are; a new shared-memory arena lays a live value graph out at native memory layout, so calls taking ref objects, nested ref graphs, and seq[T] fields (including seq-of-object) cross the boundary too — the native side reads and mutates the same memory, synced back to the interpreter. Additive and dormant: without the hybrid flag, execution is byte-for-byte unchanged from v0.3.2, and anything not safely marshalable falls back to interpretation.
aowlsem, the from-scratch semantic checker, spent the day closing byte-level gaps against the reference compiler's own typed output. The method is deliberately simple: write a small valid program that exercises one language feature, run both checkers, and diff the results token-for-token — every difference is a bug to fix or a deliberate lowering choice to record. It is now ~21.7k lines of self-hosted Nimony across 700+ commits, with the byte-exact differential corpus holding at 500/500 modules, a companion accept/reject agreement gate at 10/10, and the whole std/system checking clean.
- Generic
ref objecttypes reached full byte-identity. Yesterday they instantiated structurally; today they match the reference exactly.Container[int](…)now constructs a real heapref(newobj) instead of a value; a generic instance's synthesized lifetime hooks (=destroy/=copy/…) no longer bake in the defining module (the instance is content-addressed already); and the underlying object half numbers its type parameterT.1to the alias'sT.0, matching how the reference counts aref object's two declarations. - Value objects that carry methods. An inheritable object with managed fields that also declares
methods (type Animal = object of RootObj … method sound(a: Animal)) was emitting the full four-hook lifetime form; the reference emits only the user-method vtable table, because a type with a real vtable routes its own destruction through the vtable. The trigger is the presence of a user method, not inheritability — a common polymorphism pattern, now byte-identical. - A run of smaller parity fixes. Generic variants resolve their named-branch fields (
o.valinside a genericunwrap[T]);{.borrow.}operators returning a distinct type convert their result back (+(Celsius, Celsius) →(dconv Celsius …));untyped/typedtemplate parameters are now wildcards, sotemplate twice(x: untyped)inlines at the call site; and boolcaselabels emit the literal(true)/(false)tags. - Reading a variable before it is set is now an error — a whole class of real bugs the checker catches. Like the reference compiler, aowlsem now rejects
var x: int; return x: a local read on a path where nothing has assigned it yet (alsodiscard x, ors.add …on an untouchedvar s: seq). The definite-assignment flow analysis was already there for a related single-assignment check; today it started reporting. A branch only counts as initializing a variable when every path does, so a value set in just one arm of anifwith noelseis still flagged, while an exhaustivecaseor anif/elsethat covers both sides is accepted — matching the reference exactly. This is what lifts the new accept/reject agreement gate to 10/10. var-returning calls as assignment targets.first(c) = 99, wherefirstreturnsvar int, now writes through the location the call yields, exactly as the reference does. And avar-returning proc whose body forwards another such call (result = c.items[0]) emits the bare pointer-to-pointer copy instead of a redundant address-of-a-dereference — the two halves of that pattern now line up byte-for-byte. (These emission sites were pinned by stepping aowlsem itself under aowli's interactive debugger.)
Earlier in the day the same grind landed lambdas/anonymous procs as expressions, cross-scope iterator resolution (a local variable named like an iterator no longer hides it), custom []/[]=/{}/contains operators, multi-index x[i, j] read and write (two assertion crashes fixed), and a batch of cross-module import-resolution fixes in the driver — plus a relative include that now resolves straight from its already-parsed artifact, with no source file on disk required. The regression gates stayed green throughout: 500/500 corpus, 64/64 diagnostics, the new 10/10 accept/reject agreement, and std/system inside a four-line window.
aowli's debugger can now pause a running program and step through it interactively, instead of only printing breakpoint snapshots after a run finishes. Three additions, all in aowlcode 0.6.13 and documented under Debugging:
- Interactive stepping (
--session). Run a program once and keep it paused between commands: step into a call, step over it, run until the current routine returns, or continue to the next breakpoint. You can set breakpoints while it's paused and inspect the current frame — without re-running the program for each look. In the plugin this is the newdebug_sessiontool. - Readable output for big values. A large local — say a compiler's context object full of lookup tables — used to print as thousands of lines. Values are now rendered under a size budget and the rest is elided with a marker, so a frame dump stays readable regardless of how large the values are.
- Drill into one field. Rather than print a whole value, name the part you want —
expand c.currentModule.name,expand xs.3.field— and only that piece is shown. Object fields resolve by name, seq/array elements by index.
Also fixed a build issue where a rebuilt debugger binary could be shadowed by an older copy earlier on the lookup path. The binary now reports its build version (aowli-dbg --version) and installs to one canonical location. Full command reference: aowlcode → Execution.
Released aowli v0.3.2 — two shipped-runtime correctness fixes surfaced by running a real argument parser under the interpreter: s[a..b] / s[a..<b] slices returned only the first element instead of the substring, and a non-string value (a nil/default) could compare == equal to a string. Both fixed; byte-identical to a native compile on the repro, and the differential corpus stays at 77/77. Hardened binaries (obfuscated IR + licence gate + stripped) with SHA256 are on the release page.
aowlsem, the from-scratch semantic checker, keeps closing on full parity with the reference compiler. It is now ~18.6k lines of self-hosted Nimony across 550+ commits, and its byte-exact differential corpus stands at 498/498 modules matching nimony's own typed output, with the entire std/system checking clean (0 diagnostics). Today's work brought generic type instantiation in line with the compiler's own behavior:
- Generic sum types construct by inference.
let d = Some(99)works outOption[int]from the argument, sod.valis anintandd.val == 99resolves to a single integer comparison instead of a 25-way overload set — the same inference drives annotated conversions (Option[int](x)) and two-parameter sums likeEither[int, string]. - Plain generic value objects infer their instance too —
Pair(first: 1, second: 2)picksPair[int]straight from its field values. - Generic
ref objecttypes instantiate in full. A recursiveTree[T] = ref objectvariant now emits both halves the compiler expects — the reference alias and its underlying object type — each carrying its own per-instance lifetime hooks (destroy / move / copy), with matching typevar numbering, and its constructors (Branch(…),Leaf(…)) build the concrete instance rather than the generic origin.
aowlmcp now speaks the MCP 2026-07-28 spec — the biggest MCP revision since launch, and a clean break with its stateful past. The library serves both protocol versions, negotiated per request, so upgrading a client is never a flag day:
- Stateless core. No
initializehandshake and noMcp-Session-Id: every request carries its own context in_meta(protocolVersion/clientInfo/clientCapabilities), and a newserver/discoverRPC advertises capabilities up front (it doubles as the stdio back-compat probe). Any instance can serve any request. - Multi-Round-Trip Requests (MRTR). A tool can return an
InputRequiredResultto elicit user input mid-call; the client re-issues withinputResponsesand the echoedrequestState.registerToolMRTR+newInputRequired. - Tasks extension.
registerTaskToolreturns a task handle;tasks/get/tasks/cancel/tasks/updatedrive it — advertised inserver/discoveronly when a task tool exists. - Caching + routing.
tools/listgainsttlMs/cacheScope; the Streamable-HTTPMcp-Method/Mcp-Namerouting headers are accepted. Roots / Sampling / Logging are deprecated (aowlmcp never shipped them).
Every new flow is proven over all three transports — stdio, HTTP, and HTTP/3 (QUIC) — because they share one transport-agnostic dispatch core: stdio 27/27, HTTP 15/15 (routing headers + MRTR + Tasks over the wire), HTTP/3 4/4. Docs updated alongside.
Also released aowli v0.3.1 — the interpreter now runs the Nimony semantic checker itself, byte-identical to a native compile. Pointing the interpreter at aowlsem — a real, compiler-grade program — and diffing against native turned up three root-cause bugs; fixing them brought the run to 520/520 tokens identical. This means aowlcode's debug/trace can now be aimed at the compiler's own passes.
- Fully-initialised pointer values. Constructing an interior
ptrleft its flat-memory view fields (region/foff/elemBits/base) uninitialised, so a later read saw garbage — a genuine memory-safety class, not a cosmetic gap. Caught with valgrind running under mimalloc's Valgrind-tracking build (mimalloc otherwise bypasses the sanitizer); every pointer construction now initialises all fields. seqappend value-copy.s.add xnow copiesxon the way in — the same=copyenvelope semantics v0.3.0 gave assignment — so mutating the appended element never aliases the source.- Content-addressed tag dedup.
StringView.==is gated so NIF tag interning deduplicates by identity, the wayTokenBuf-style content-addressed programs expect.
The debugger got sharper alongside it. --break-func:NAME and file-scoped --break:file.nim:LINE resolve routines through the include chain (a bare line number is ambiguous once modules are merged), and program_args forward to the interpreted program's commandLineParams() — so a breakpoint can land inside semCall while the checker runs on a real input.
A plugin-packaging note. The aowlcode MCP server a session runs isn't the marketplace checkout or the newest cached build — it's the exact version pinned in installed_plugins.json. Ours was stuck several versions back at 0.6.0, which silently dropped any newer argument (like program_args) before it reached the handler. The fix is a one-line pin bump (to 0.6.9) plus a restart; the thing to watch for is a parameter that's present in the source but missing from the live tool's schema.
Created aoughwlup and aoughwl
Created aoughwl-code and aoughwl-code-release
Released aowli v0.3.0 — the correctness-complete build. Both engines — the tree-walker behind the public aowli-interp / aowli-dbg, and the internal bytecode VM — now hit zero in-scope divergence across a 423-program differential corpus run against the nimony compiler. The engines agree with each other and with native, program for program.
Value semantics landed. Assigning or binding a value object / tuple / value-array now copies the envelope (refs stay shared) — var x = a; x.a = 999 no longer reaches back and mutates a. This was the last big place aowli's aliasing quietly disagreed with the real =copy.
The last OS-boundary gaps closed. Real host stat / lstat (so fileExists / dirExists are correct), pointer identity in == / !=, cast[int](ptr) round-tripping through flat memory, and VM argv / stdin seeding. Plus a sweep of narrower fixes: float→int conversion, block-expression values, cyclic-import init order, a self-nested-iterator hang, and Table element write-back.
The one boundary the pure value interpreter can't cross by itself is literal-C: {.emit.} and C FFI have no C to run inside the value model. That's exactly what hybrid-native mode absorbs — and today it ran real foreign C for the first time.
Hybrid-native executed real C-FFI. aowli's hybrid mode — interpret most modules, run selected ones as native code — now offloads a header-backed {.importc.} proc to a compiled shim and calls into genuine C: a static inline cadd crossed the boundary, ran natively, and marshaled its result back into the interpreter byte-identical to native. The proc-offload path (pure-nimony procs run native) also picked up a permanent regression lane. This is interpreter-development progress, not yet in the public v0.3.0 binary; the remaining piece is top-level {.emit.} / importc-var in the main module.
Playground refresh. The in-browser playground got a round of work: rebuilt engine bundles (fixing a stale-bundle bug where Bytecode-VM and Native-JS runs showed no output), a unified Pipeline config panel (parser · checker · lowering · engine), aowlsem rebuilt from latest, the two source-pane toolbars merged into one, a clearer footer with an inline engine picker, and curly-brace block mode no longer false-flags a { … } body as "not a Nim block."
Gave the whole stack one source of truth for how values are laid out: aowlabi. Three places each kept their own copy of how is a string / seq / object / ref actually represented — the interpreter, the C backend, the JS backend — and they had quietly drifted. aowlabi is now the single canonical answer:
- the size / alignment / field-offset engine — one implementation of the C-struct layout rules, parameterized by pointer size
- the canonical heap-block spec — string SSO +
LongString{fullLen,rc,cap,data}, seq{len,data}, the ARC ref box{rc,data}— as named offset constants, one truth - the marshal matrix — which types cross a native boundary by value / by buffer / by fallback, plus the JS representation mapping (fast
numbervs faithfulbigint, char, tuple, and so on)
aowlc, aowljs and aowli all read the same spec now instead of re-deriving it.
aowli grew a real runtime layer. The scattered places where the interpreter crossed from its value world into a faster / foreign executor — host natives, flat memory, syscalls, the miss policy — are one spine now: a provider registry (interpret · host-native · syscall · hybrid-native), a codec (identity · flat C-ABI · JS value), and a policy that is never silently wrong — an unsupported crossing fails loud or falls back to interpret, never a wrong answer.
And the payoff — hybrid execution. aowli can now interpret only the file you care about and run every other module as natively-compiled code at full speed. Debug one file slowly, with full observability, while its libraries run native. aowli auto-generates C-callable shims for the cross-boundary calls, marshals scalars, POD objects / tuples, strings and seqs across using aowlabi's layout, and dispatches at the call site. Every result is byte-identical to the fully-native build; anything that can't be safely marshaled (refs, closures) transparently falls back to the interpreter — so it is faster where it can be and correct everywhere.
aowli --hybrid --interpret:mymod prog # mymod stays observable, everything else runs native
Rebuilt the net stack around a single-threaded async reactor: one OS thread, epoll, passive-proc coroutines, no std async or thread pool.
- HTTP/1.1: keep-alive, chunked, 300/300 concurrent
- WebSocket: masking, frame/control validation, fragmentation, incremental UTF-8, close validation,
permessage-deflate; 19/19 conformance, 160/160 echo - HTTP/3: ngtcp2 + nghttp3 + GnuTLS behind a small pull API; 20 QUIC clients, one thread, ASan/LSan clean
- RFC 9221 datagrams + WebTransport datagrams over H3; streams remain
Created aowljson: reusable JSON values, error-as-value parsing, serializer, builders, v{"key"}, v.at(i).
Created aowlmcp: transport-independent MCP dispatch over stdio, HTTP, and HTTP/3. Tests: 13/13, 6/6, 4/4. Includes compile diagnostics and NIF outlines through aowlcode.
aowli became an actual runtime: flat memory, casts, copyMem, allocation, unchecked arrays, fd-backed file I/O, env access, ownership hooks, refcounted ref objects, and fail-fast unsupported stdlib calls. It now runs about 92% of compiler-buildable programs, with no known silent wrong-result cases. Remaining: some OS/VM gaps, threads, async.
Released aowli-release v0.1.0 with:
aowli-interp: run typed NIF, optional call-tree traceaowli-dbg: batch breakpoints and structured frame dumps- stripped binaries, fail-closed licence gate, SHA256, VirusTotal links
- no source paths or internal proc/type names
Updated aowlcode with trace/debug tools, /land, Haiku appliers, and parallel edit application.
Back to work after a couple of quiet days.
A quiet day.
Shout-out to a fellow Nim'er's project — 3code, worth a look.
More aowlsem — the whole day is a generics push. The semchecker now instantiates and preserves generic constructs end to end: typevar calls and signatures, generic object applications with substituted field types and attached hooks, generic array bounds and range iterators, generic seq index reads, var forwarding through generic params, late-bound generic hook calls, and quoted generic operators. Around it: out parameter type resolution, sink/source normalization, typed pointer comparisons lowered to magics, unchecked-pointer index assignments wrapped, requires pragma expression checking, and threadvar globals emitted. Steady, surgical commits — aowlsem is now past 340 total commits since Tuesday.
A major day for aowlsem — 126 commits landing the clean-room semchecker's core. It now passes 397/397 corpus fixtures byte-exact against the nimony oracle, and — the milestone — it does a complete zero-diagnostic traversal of the full std/system: the whole system.nim plus its included std/system/*.nim set, ~6,383 lines, semcheck with 0 errors and 0 log lines. Full-system parity against nimony's own output is down to ~33k canonical diff lines from an earliest baseline of ~62k — a 46.5% reduction, with the first mismatch now a third of the way into the semantic output.
Under that headline: the magic table (arithmetic / comparison / set / pointer magics), varargs[T] params with call-site collection, membership (x in coll) generalized across seq/array/string, seq slicing and s[a..b], for (a, b) in … tuple destructuring, ^k backwards indexing, countdown typevar inference, concept declarations, lifetime-hook attachment, and ptr UncheckedArray[T] indexing. aowlsem also grows diagnostics that go beyond nimony — E0205 self-comparison, E0206 unsigned-compared-to-zero, E0207 empty-loop-range, E0208 tuple-index-out-of-bounds, E0209 shift-amount-out-of-range. (source private for now; docs public, access on request)
We also stood up the whole distribution story — private components, public binaries. The plan is simple: anything that stays source-private, we still ship to everyone — obfuscated, inside a stripped binary.
- obfuscate was reworked to be IR-only. It operates entirely on the compiler's own NIF/AIF token tree, never on source text, so it inherently can't corrupt runtime data — strings, chars and comments are their own token kinds and are never touched. Its
obfnifpass renames every declared symbol to an opaque token (by spelling on parsed NIF, symbol-precise on typed NIF) and weaves in behaviour-preserving control flow, then the result re-feeds the pipeline and behaves identically. - aowl-release is the hardening harness — a
build-release.shthat wraps each component's own build and layers source obfuscation → a fail-closed licence/version gate → NIF control-flow injection →--strip-all(drops the symbol table decompilers love). The gate refuses to run an expired build; there's no risky client-side kill-switch. - Five
-releaserepos now exist as the public homes for the currently-private stages — aowlsem-release, aowli-release, aowlts-release, aowlpy-release, aowlweb-release. Source stays private; the obfuscated, gated, stripped binaries land here shortly so anyone can run the full stack.
And aowlup — rustup for the aowl/nimony stack. It installs, versions, and selects the pipeline: every slot has interchangeable variants (parser aowlparser|nifler, sem aowlsem|nimsem, hexer aowlhexer|hexer, plus backends and tooling), grouped into one-command profiles (aowl = all ours, nimony = all theirs, hybrid = the driver default), each pinned to a git version with a GitHub update check (it doubles as a nimony version manager). aowlmony then compiles against whatever aowlup has selected — exactly the rustup : cargo split. The -release binaries plug straight into this: aowlup is how you'll pull, pin, and select them, and aowlup +nimony gives one-shot toolchain overrides.
And the playground grew a semantics choice. aowlsem now runs in the browser — you can pick aowl semantics instead of the default nim semantics when type-checking, right in the playground. It's marked experimental: real and checking a substantial slice today, but not the full stdlib or generics yet, so it grows from here. aowlsuggest moved into the playground too — its quick-fix / lint layer now runs client-side over the parser's diagnostics, so fix-its surface as you edit — and aowlparser got another update, with the latest parser bundle now shipping in the playground.
A heavy day on the front and middle of the pipeline.
aowlparser — reached full 310/310 structural parity with the upstream Nim standard library: the entire stdlib round-trips. Shipped a real check lint mode — grammar-level error detection with fix-its and source-ordered diagnostics: assignment = where == was meant, a for missing its in, identifier-expected on let/const, and more. Fixed three parser hangs (infinite recursion) and hardened the lexer — UTF-8 identifiers, BOM stripping, custom numeric literals (N'big), parenthesized proc literals, term-rewriting template patterns.
aowlsem — a big step toward a true drop-in: an auto-import system that pulls in system and the module's own imports with no manual flags, real include splicing, when not defined(...) folding, definite-assignment that honours noinit/threadvar/importc, typedesc modelled as a type, templates as an overload set, accent-quoted/operator routine names, and the first value-object ARC hook synthesis — the foundation for Table. (source private for now; docs public, access on request)
aowli — the interpreter/VM now reads the shared aowlhl HL-IR layer (hlload / hlclassify / hlwalk) instead of its own tree-walk, and gained dynamic method dispatch with field write-through for ptr/var receivers, closures with nested capture, and UTF-8 add(string, Rune). With this, aowli is feature-complete: it reproduces 100% of the runnable test corpus byte-for-byte against nimony's own compile-and-run, on both engines (tree-walker and bytecode VM).
aowlhl is now the shared high-level IR — one Nim→HL-IR reader that both aowli and aowljs consume, so the interpreter and the JavaScript backend classify and walk the same skeleton. One lowering, many emitters.
The docs site got a ground-up rebuild. Migrated aoughwl.github.io off Jekyll / just-the-docs onto VitePress — it's now a single-page app with instant client-side navigation (no full reloads), a collapsible nested sidebar, a near-black dark theme, local search, and self-hosted fonts (no font or page flash). It deploys through a GitHub Actions workflow instead of Jekyll, and the in-browser playground is preserved byte-identically.
The nav is region-grouped — Overview / Pipeline / Emitters / Tools / Libraries — and every pipeline, emitter, and tool row carries a small right-aligned "↗" straight to that project's GitHub repo. A floating theme toggle sits in the corner, and GitHub · Discord · Support links live in the top bar.
Repositioned: aoughwl is a ground-up Nimony toolchain. Wrote the interop contract — AIF ≡ NIF, byte-for-byte, so any Nim/Nimony program behaves identically. Renamed the compiler stages aif* → aowl* (aowlparse / aowlsem / aowlhexer / aowlc / aowljs / aowli / aowlmony) — aif now names the format only — and nim-code → aowl-code. Reworked the docs into two homes — Documentation (terse reference) and Engineering Notes (opinionated writeups) — collapsed the changelog into a single Changes record, and normalized every repo description + topics across the org. aowlsem and aowlhexer stay private for now (docs public, access on request); the playground moves onto the new sem + hexing shortly.
Created aifhexer
Created aif
Created aifmony
Created aifc
Created aifjs
Created aifjs-js
Created aifsem
Updated aifi
Updated aifparser
Updated nimony-playground
Took nifi private.
Updated nifi — 6–10× performance gain.
Updated nimony-playground
Updated nifparser
Updated nifi
- Added curly bracket support to the nifparser
- Finalized nifparser, passing against the full nimony suite- byte identical to niffler
- Nearly finished nimony-playground, missing small QOL and a final port to the aoughwl ecosystem (I cannot wait)
Created nimony-playground
Created nifparser
Created nifi, a Nimony NIF Interpreter
Created aowl-lsp, it's nimony-lsp, but with a universal plugin system. Obtain novel features!
Created vscode-aowl, aoughwl hosted on-machine within VSCode
Finalized the aoughwl core spec.
Noticed a memory bug exists- likely roots as a true Nimony bug which interacts with niflens and my nim-code instances
Created nifrewrite makes NIF rewrites simple
Fixed IC
Updated niflens
Updated nimony-lsp
Our VSCode extension + Nimony LSP is nearly as performant and featurefull as it can be, live diagnostics work phenomenally as you type... more to come here.
Pushed IC to aoughwl/Nimony: ~1s -> ~10ms
see: ic-parallel-deps, ic-cursor-traversal, ic-warm-daemon, ic-batch-intern
Created niflens, a CLI tool for parsing and viewing NIF
Updated nimony-lsp and nim-code to benefit from niflens — live diagnostics and suggestions now work as you type!
Massively expanded the net stack — now 8 one-concern repos:
• tls — TLS 1.3 over OpenSSL 3, client + server (SNI, ALPN, verification); pulled into its own repo
• serve — HTTPS, a concurrent worker pool, HTTP/2 (nghttp2: h2c + ALPN h2), chunked request bodies + Expect: 100-continue, opt-in gzip/br compression
• dual-stack IPv6 across tcp / net — one listener serves v4 + v6
• new compress repo — one-shot gzip / brotli / zstd codecs
• ws — a nimony-native WebSocket (RFC 6455), server + client, ws:// and wss://
• HTTP/3 in requests (useHttp3) via curl-impersonate's bundled ngtcp2
Every layer is tested against real clients — curl --http2, live wss://, TLS 1.3 handshakes.
Today starts the official aoughwl/nimony fork.
This is now the main place my Nimony work will go:
- features
- bug fixes
- more opinionated, dynamic, and substantial stdlib
We also shipped aoughwl/nimony-lsp and aoughwl/nim-code:
nimony-lspis the language-server sidenim-codeis a Claude Code plugin and MCP server focused on reducing token usage with Nim and Nimony
Effective indefinitely, the Nimony JavaScript/TypeScript/WASM/Python backend work is private.
For interested parties: the JavaScript and WebAssembly backends are ~complete and remain true to the original vision.
I will gladly and promptly add anyone who wants access, but you will need to reach out to me directly over Discord (timbuktu_guy)
