Skip to content

Commit ea7b995

Browse files
committed
Update public docs for v0.4.0 release
- README: 1130+ vars, pprint 26/26, zone architecture, Linux verified - ARCHITECTURE: zone overview, file paths updated to engine/lang/app - DIFFERENCES: spec/reducers/pprint now fully implemented, 55 skipped - CHANGELOG: v0.4.0 entry (HAMT, zone arch, coverage, CI) - NOTICE: 68 upstream test files - docs/compatibility: 68 upstream tests, 83 total namespaces - docs/wasm-ffi: Wasm 3.0, 523 opcodes, zwasm JIT
1 parent 8b98862 commit ea7b995

File tree

8 files changed

+119
-69
lines changed

8 files changed

+119
-69
lines changed

.dev/memo.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ CW is a complete, optimized Zig implementation with behavioral Clojure compatibi
2525

2626
## Current Task
2727

28-
None — awaiting user direction.
28+
v0.4.0 release — all docs updated, ready for tag.
2929

3030
## Previous Task
3131

32-
Zone Cleanup Task 4: Isolate test-only imports in engine/ (12 → 0 violations).
32+
HAMT crash fix + CI benchmark timeout fix + full doc update for v0.4.0.
3333

3434
## Task Queue
3535

ARCHITECTURE.md

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,22 @@
22

33
This document describes the internal architecture of ClojureWasm.
44

5+
## Zone Architecture
6+
7+
CW uses a strict 4-zone layered architecture. Lower layers never import
8+
from higher layers. Zone dependencies are enforced by CI (0 violations).
9+
10+
```
11+
Layer 0: src/runtime/ — Value, collections, GC, environment
12+
Layer 1: src/engine/ — Reader, Analyzer, Compiler, VM, TreeWalk
13+
Layer 2: src/lang/ — Built-in functions, interop, lib namespaces
14+
Layer 3: src/app/ — CLI, REPL, deps.edn, Wasm bridge
15+
```
16+
17+
When a lower layer needs to call a higher layer, the vtable pattern
18+
is used (`runtime/dispatch.zig`): the lower layer defines function
19+
pointers that the higher layer sets during initialization.
20+
521
## Pipeline Overview
622

723
ClojureWasm processes Clojure source code through a four-stage pipeline:
@@ -43,7 +59,7 @@ by `EvalEngine.compare()` tests.
4359

4460
## Reader
4561

46-
**Files**: `src/reader/reader.zig`, `src/reader/tokenizer.zig`, `src/reader/form.zig`
62+
**Files**: `src/engine/reader/reader.zig`, `src/engine/reader/tokenizer.zig`, `src/engine/reader/form.zig`
4763

4864
The reader converts source text into a Form tree (a syntax tree before
4965
semantic analysis). It handles:
@@ -57,7 +73,7 @@ semantic analysis). It handles:
5773

5874
## Analyzer
5975

60-
**Files**: `src/analyzer/analyzer.zig`, `src/analyzer/node.zig`
76+
**Files**: `src/engine/analyzer/analyzer.zig`, `src/engine/analyzer/node.zig`
6177

6278
The analyzer transforms Forms into Nodes (an executable AST). It resolves
6379
variable bindings, expands macros, and compiles regex patterns at analysis
@@ -71,8 +87,8 @@ Key responsibilities:
7187

7288
## Compiler + VM
7389

74-
**Files**: `src/compiler/compiler.zig`, `src/compiler/opcodes.zig`,
75-
`src/compiler/chunk.zig`, `src/vm/vm.zig`
90+
**Files**: `src/engine/compiler/compiler.zig`, `src/engine/compiler/opcodes.zig`,
91+
`src/engine/compiler/chunk.zig`, `src/engine/vm/vm.zig`
7692

7793
The compiler transforms Nodes into bytecode stored in Chunks. Each
7894
instruction is a fixed 3-byte format: u8 opcode + u16 operand.
@@ -110,7 +126,7 @@ The VM is a stack-based machine with:
110126

111127
### ARM64 JIT
112128

113-
**File**: `src/vm/jit.zig`
129+
**File**: `src/engine/vm/jit.zig`
114130

115131
A proof-of-concept JIT compiler for hot integer loops on ARM64.
116132

@@ -122,7 +138,7 @@ A proof-of-concept JIT compiler for hot integer loops on ARM64.
122138

123139
## TreeWalk Interpreter
124140

125-
**File**: `src/evaluator/tree_walk.zig`
141+
**File**: `src/engine/evaluator/tree_walk.zig`
126142

127143
The TreeWalk interpreter evaluates Nodes directly without compilation.
128144
It maintains a local binding stack (256 slots) and closure capture.
@@ -173,7 +189,7 @@ allocations are cached (up to 4096 blocks per pool) for O(1) reuse.
173189

174190
## Bootstrap
175191

176-
**File**: `src/runtime/bootstrap.zig`
192+
**File**: `src/engine/bootstrap.zig`
177193

178194
ClojureWasm uses a two-phase bootstrap:
179195

@@ -193,11 +209,11 @@ in ~4ms by skipping the parse/analyze/eval cycle for core.clj.
193209

194210
## Wasm Runtime
195211

196-
**Files**: `src/wasm/*.zig`
212+
**Files**: `src/app/wasm/*.zig`, `src/runtime/wasm_types.zig`
197213

198-
A built-in WebAssembly interpreter supporting 461 opcodes (225 core + 236
199-
SIMD). Clojure code can load and call Wasm modules via the `cljw.wasm`
200-
namespace.
214+
The Wasm runtime is powered by [zwasm](https://github.com/clojurewasm/zwasm),
215+
supporting 523 opcodes (236 core + 256 SIMD + 31 GC). Clojure code can load
216+
and call Wasm modules via the `cljw.wasm` namespace.
201217

202218
Key components:
203219
- **Module parser**: Decodes Wasm binary format (types, functions, tables,
@@ -207,13 +223,11 @@ Key components:
207223
- **VM**: Stack-based interpreter with switch dispatch over predecoded IR
208224
- **WASI**: File I/O, clock, random, args, environ
209225
- **Multi-module linking**: Cross-module function imports
210-
- **SIMD**: v128 type with 236 SIMD opcodes
226+
- **SIMD**: v128 type with 256 SIMD opcodes
227+
- **GC proposal**: 31 GC opcodes (struct, array, ref types)
211228

212-
Performance: The interpreter is ~10-30x slower than wasmtime (JIT compiler)
213-
for compute-heavy modules. This is the fundamental interpreter-vs-JIT gap —
214-
wasmtime compiles Wasm to native machine code via Cranelift, while ClojureWasm
215-
dispatches predecoded instructions one at a time. Module load time is faster
216-
(~4ms vs ~5ms) since no compilation step is needed.
229+
Performance: zwasm uses Register IR with ARM64/x86_64 JIT compilation,
230+
winning 14/23 benchmarks against wasmtime with a ~43x smaller binary.
217231

218232
## Regex Engine
219233

@@ -225,7 +239,7 @@ at runtime.
225239

226240
## nREPL Server
227241

228-
**Files**: `src/repl/nrepl.zig`, `src/repl/bencode.zig`
242+
**Files**: `src/app/repl/nrepl.zig`, `src/app/repl/bencode.zig`
229243

230244
A CIDER-compatible nREPL server supporting 14 operations: eval, load-file,
231245
complete, info, lookup, stacktrace, clone, close, describe, ls-sessions,

CHANGELOG.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,38 @@
11
# Changelog
22

3+
## v0.4.0 (2026-02-25)
4+
5+
### Architecture
6+
- 4-zone layered architecture (D109): runtime → engine → lang → app
7+
- Zone dependencies enforced by CI gate (0 violations, down from 126)
8+
- Vtable pattern for dependency inversion (runtime/dispatch.zig)
9+
- HAMT (Hash Array Mapped Trie) for persistent hash maps with collision nodes
10+
11+
### Coverage
12+
- 1,130/1,243 vars implemented (90.9%), 651/706 clojure.core (92.2%)
13+
- clojure.pprint: 26/26 (was 22/26), full cl-format support
14+
- clojure.spec.alpha: 87/87, clojure.spec.gen.alpha: 54/54
15+
- clojure.core.reducers: 22/22, clojure.datafy: 2/2, clojure.instant: 3/5
16+
17+
### Testing
18+
- 83 Clojure test namespaces (68 upstream ports), all passing
19+
- Full Mac/Linux test symmetry (macOS aarch64 + Linux x86_64)
20+
- 6 e2e tests, 14 deps.edn e2e tests
21+
22+
### Bug Fixes
23+
- HAMT crash when function values used as map keys (>8 entries promote to HashMap)
24+
- Identity hash (splitmix64) for function types replacing constant hash
25+
- HAMT collision node support for full 32-bit hash collisions
26+
27+
### Performance
28+
- Binary: 4.76MB (ReleaseSafe)
29+
- Startup: ~4ms
30+
- RSS: ~7.6MB
31+
32+
### CI
33+
- Cross-platform CI: macOS 14 + Ubuntu 24.04 + 4 cross-compile targets
34+
- Benchmark smoke tests with portable timeout (perl alarm)
35+
336
## v0.3.0 (2026-02-20)
437

538
### Architecture v2

DIFFERENCES.md

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -42,41 +42,41 @@ Java standard library functionality is replaced with Zig equivalents:
4242

4343
## Namespaces
4444

45-
### Not Implemented
45+
### Not Implemented (JVM-only)
4646

4747
| Namespace | Reason |
4848
|--------------------------|--------------------------------------|
49-
| clojure.spec.alpha | Complexity; out of scope for alpha |
50-
| clojure.spec.gen.alpha | Depends on spec |
51-
| clojure.core.specs.alpha | Depends on spec |
52-
| clojure.core.reducers | Requires reify + ForkJoin |
53-
| clojure.core.server | Socket REPL (nREPL used instead) |
5449
| clojure.reflect | JVM reflection |
5550
| clojure.inspector | JVM Swing UI |
56-
| clojure.java.browse | JVM Desktop class |
5751
| clojure.java.javadoc | JVM-specific |
58-
| clojure.main | JVM entry point |
59-
| clojure.instant | JVM Date/Timestamp |
60-
| clojure.datafy | Requires protocols not yet available |
52+
| clojure.test.junit | JUnit integration |
6153

6254
### Partially Implemented
6355

64-
| Namespace | Done | Total | Notes |
65-
|----------------|------|-------|-----------------------------|
66-
| clojure.core | 637 | 706 | 90% — see skip list below |
67-
| clojure.test | 32 | 39 | No spec integration |
68-
| clojure.pprint | 9 | 26 | Core pprint + print-table |
69-
| clojure.repl | 11 | 13 | No spec, no Java reflection |
70-
| clojure.edn | 1 | 2 | read-string only (no read) |
71-
| clojure.data | 3 | 5 | No protocol-based dispatch |
56+
| Namespace | Done | Total | Notes |
57+
|-----------------------|------|-------|-----------------------------|
58+
| clojure.core | 651 | 706 | 92% — see skip list below |
59+
| clojure.core.protocols| 10 | 11 | CollReduce, IKVReduce |
60+
| clojure.core.server | 7 | 11 | No socket REPL |
61+
| clojure.instant | 3 | 5 | No Calendar/Timestamp |
62+
| clojure.java.process | 5 | 9 | No async process |
63+
| clojure.main | 16 | 20 | REPL, script loading |
64+
| clojure.repl | 11 | 13 | No Java reflection |
65+
| clojure.test | 38 | 39 | No spec integration |
66+
| clojure.xml | 7 | 9 | Pure Clojure parser |
7267

7368
### Fully Implemented
7469

75-
clojure.string (21), clojure.set (12), clojure.math (45), clojure.walk (10),
76-
clojure.zip (28), clojure.template (2), clojure.stacktrace (6),
77-
clojure.java.io (7), clojure.java.shell (5), cljw.http (6).
70+
clojure.core.reducers (22), clojure.core.specs.alpha (1),
71+
clojure.data (5), clojure.datafy (2), clojure.edn (2),
72+
clojure.java.browse (2), clojure.java.io (19), clojure.java.shell (5),
73+
clojure.math (45), clojure.pprint (26), clojure.repl.deps (3),
74+
clojure.set (12), clojure.spec.alpha (87), clojure.spec.gen.alpha (54),
75+
clojure.stacktrace (6), clojure.string (21), clojure.template (2),
76+
clojure.test.tap (7), clojure.walk (10), clojure.zip (28),
77+
cljw.wasm (17), cljw.http (6).
7878

79-
## Skipped clojure.core Vars (71 of 706)
79+
## Skipped clojure.core Vars (55 of 706)
8080

8181
### JVM Class System (~25 vars)
8282

@@ -177,7 +177,7 @@ etc.) is not replicated; use `Exception` for general catches.
177177

178178
## Platform
179179

180-
- **Verified**: macOS Apple Silicon (aarch64-macos)
181-
- **CI passes**: Linux x86_64, Linux aarch64
180+
- **Verified**: macOS Apple Silicon (aarch64-macos), Linux x86_64
181+
- **Cross-compiles**: Linux aarch64, macOS x86_64
182182
- **Not tested**: Windows, other architectures
183183
- **ARM64 JIT**: Only active on aarch64; no-op on other architectures

NOTICE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Clojure
1111
The following components are derived from upstream Clojure:
1212
- src/clj/ — Clojure standard library (core, set, walk, test, etc.)
1313
with UPSTREAM-DIFF modifications for the Zig runtime
14-
- test/upstream/clojure/ — Ported test suite (39 files)
14+
- test/upstream/ — Ported test suite (68 files)
1515

1616
SCI (Small Clojure Interpreter)
1717
Copyright (c) Michiel Borkent. All rights reserved.

README.md

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
> behavioral differences from reference Clojure. Bugs and rough edges are
1212
> expected. See [DIFFERENCES.md](DIFFERENCES.md) for details.
1313
>
14-
> **Verified on**: macOS (Apple Silicon / aarch64). Linux x86_64 and
15-
> aarch64 builds pass CI but have not been extensively tested.
14+
> **Verified on**: macOS (Apple Silicon / aarch64) and Linux (x86_64).
15+
> Cross-compiles to aarch64-linux. All test suites pass on both platforms.
1616
1717
A Clojure runtime written from scratch in Zig. No JVM, no transpilation —
1818
a native implementation targeting behavioral compatibility with Clojure.
@@ -25,7 +25,7 @@ a native implementation targeting behavioral compatibility with Clojure.
2525
- **Wasm FFI** — call WebAssembly modules from Clojure (523 opcodes including SIMD + GC)
2626
- **Dual backend** — bytecode VM (default) + TreeWalk interpreter (reference)
2727
- **deps.edn compatible** — Clojure CLI subset (-A/-M/-X/-P, git deps, local deps)
28-
- **1100+ vars** across 30+ namespaces (651/706 clojure.core)
28+
- **1130+ vars** across 30+ namespaces (651/706 clojure.core)
2929

3030
## Getting Started
3131

@@ -131,7 +131,7 @@ Known divergences are documented in [DIFFERENCES.md](DIFFERENCES.md).
131131
| clojure.test | 38/39 | Test framework |
132132
| clojure.test.tap | 7/7 | TAP output formatter |
133133
| clojure.repl | 11/13 | doc, dir, apropos, source, pst |
134-
| clojure.pprint | 22/26 | Pretty printing, print-table |
134+
| clojure.pprint | 26/26 | Pretty printing, print-table |
135135
| clojure.stacktrace | 6/6 | Stack trace utilities |
136136
| clojure.main | 16/20 | REPL, script loading, ex-triage|
137137

@@ -220,25 +220,26 @@ src/
220220
│ ├── core.clj Core library (~2400 lines)
221221
│ └── string.clj, set.clj... Standard library namespaces
222222
223-
├── reader/ Stage 1: Source → Form
224-
├── analyzer/ Stage 2: Form → Node
225-
├── compiler/ Stage 3: Node → Bytecode
226-
├── vm/ Stage 4a: Bytecode execution (+ JIT)
227-
├── evaluator/ Stage 4b: TreeWalk interpreter
228-
229-
├── runtime/ Core types, GC, environment
230-
├── builtins/ Built-in functions (27 modules)
231-
├── regex/ Regex engine
232-
├── repl/ nREPL server, line editor
233-
└── wasm/ WebAssembly runtime (523 opcodes)
223+
├── runtime/ Layer 0: Value, collections, GC, environment
224+
├── engine/ Layer 1: Reader, Analyzer, Compiler, VM, TreeWalk
225+
│ ├── reader/ Source → Form
226+
│ ├── analyzer/ Form → Node
227+
│ ├── compiler/ Node → Bytecode
228+
│ ├── vm/ Bytecode execution (+ ARM64 JIT)
229+
│ └── evaluator/ TreeWalk interpreter
230+
├── lang/ Layer 2: Built-in functions, interop, lib namespaces
231+
├── app/ Layer 3: CLI, REPL, deps.edn, Wasm bridge
232+
└── regex/ Regex engine
234233
235234
bench/ 31 benchmarks, multi-language
236-
test/ 81 Clojure test files (54 upstream ports)
235+
test/ 83 Clojure test namespaces (54 upstream ports)
237236
```
238237

238+
Strict 4-zone layered architecture: lower layers never import from higher layers.
239+
Zone dependencies enforced by CI gate (0 violations).
240+
239241
The [`.dev/`](.dev/) directory contains design decisions, optimization logs,
240-
and development notes. Some may be outdated, but may interest those curious
241-
about how the project evolved.
242+
and development notes.
242243

243244
## Benchmarks
244245

@@ -260,14 +261,14 @@ bash test/e2e/run_e2e.sh # End-to-end tests (6 wasm)
260261
bash test/e2e/deps/run_deps_e2e.sh # deps.edn E2E tests (14)
261262
```
262263

263-
81 Clojure test files including 54 upstream test ports with 600+ deftests.
264+
83 Clojure test namespaces including 54 upstream test ports with 600+ deftests.
264265
All tests verified on both VM and TreeWalk backends.
265266

266267
## Future Plans
267268

268269
- **JIT expansion** — float operations, function calls, broader loop patterns
269270
- **Generational GC** — nursery/tenured generations for throughput
270-
- **Persistent data structures**HAMT/RRB-Tree implementations
271+
- **Persistent data structures** — RRB-Tree for vectors (HAMT for maps: done)
271272
- **wasm_rt** — compile Clojure to run *inside* WebAssembly
272273

273274
## Potential Use Cases

docs/compatibility.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ Vars that are defined but have limited or no-op behavior:
107107

108108
## Upstream Test Coverage
109109

110-
63 upstream test files (62 passing, 1 failing: test_fixtures.clj).
110+
68 upstream test files, all passing.
111+
83 Clojure test namespaces total (68 upstream + 15 CW-specific).
111112
146 differential test expressions (CW vs JVM), all passing.
112113
6 core e2e tests, 14 deps.edn e2e tests.
113114

docs/wasm-ffi.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,8 @@ are caught and reported as CW runtime errors:
237237

238238
## Wasm Engine
239239

240-
CW uses [zwasm](https://github.com/clojurewasm/zwasm) v1.1.0 as its Wasm runtime:
241-
- WebAssembly 2.0 (100% spec compliance, 62K tests)
242-
- ARM64 and x86_64 JIT compilation
240+
CW uses [zwasm](https://github.com/clojurewasm/zwasm) as its Wasm runtime:
241+
- Full Wasm 3.0 support (all 9 proposals including GC, function references, SIMD, exception handling)
242+
- 523 opcodes (236 core + 256 SIMD + 31 GC)
243+
- Register IR with ARM64/x86_64 JIT compilation
243244
- WASI preview1 support

0 commit comments

Comments
 (0)