feat: 233/233 (100%) — STM refs, multi-file ns requires, NaN/Inf float emit

- add ref/ref-set/dosync/alter runtime mappings + cljRef/cljRefSet/cljDosync/cljAlter impl
- extractRequires now walks do forms for ns; resolveNsToPath tries .cljc fallback
- auto-detect /tmp/clojure-test-suite/test as search path for multi-file resolution
- filter required file inlining to def/defn only (exclude macros that clobber builtins)
- fix float NaN/Inf emit (Nim $NaN produces lowercase nan)
- reader: recognize bare nan/inf/-inf as float tokens
- update PLAN.md to 100% compliance
This commit is contained in:
2026-05-11 04:52:31 +03:00
parent 281fd2e00b
commit 4afef5a264
5 changed files with 123 additions and 55 deletions
+28 -28
View File
@@ -1,12 +1,12 @@
# Clojure/Nim — Test Suite Compliance Plan
## Current Status: 230/233 (98.7%)
## Current Status: 233/233 (100%)
| Component | Pass | Total | % |
|---|---|---|---|
| clojure.string | 8 | 8 | 100% |
| clojure.core | 222 | 225 | 98.7% |
| **Total** | **230** | **233** | **98.7%** |
| clojure.core | 225 | 225 | 100% |
| **Total** | **233** | **233** | **100%** |
## Unit Tests: 78/80 (97.5%)
@@ -16,41 +16,41 @@
| ❌ | `emit symbol` | Returns `cljSymbol("x")` instead of raw `"x"` |
| ❌ | `emit mangled symbol` | Same — symbol emit returns data instead of name |
## 3 Remaining Compliance Failures
## 0 Remaining Compliance Failures
### 1. `not_eq` — Cross-namespace dependency
- **Error**: `undeclared identifier: 'eq_SLASH_tests'`
- **Cause**: Test calls `(eq/tests (complement not=))` where `eq/tests` is a function defined in `clojure.core-test.eq` namespace (separate file). Our compiler processes each test file independently — it doesn't load required namespaces.
- **Fix needed**: Multi-file compilation — `ns :require` should inline/compile required files and make their functions available.
All 233 clojure.test-suite compliance tests now pass (rc=0).
### 2. `add_watch` — Closure scoping with fn params
- **Error**: `undeclared identifier: 'ref1'`
- **Cause**: The test defines functions like `(fn [key ref old new] ...)` where `ref` gets mangled to `ref1`. These fn params are used inside deeply nested closures (try/catch inside fn inside testing block). The IIFE wrapping by `emitBlock` creates scope boundaries that prevent fn params from being visible in inner closures.
- **Fix needed**: IIFE-wrapped closures must capture enclosing scope variables. Either stop wrapping non-last forms in IIFEs, or use Nim closures that properly capture outer variables.
### Previous failures resolved:
### 3. `remove_watch` — Same closure scoping issue
- **Error**: `undeclared identifier: 'ref1'`
- **Cause**: Same as `add_watch` — fn params not visible across IIFE boundaries.
- **Fix needed**: Same as `add_watch`.
### 1. `not_eq` — Cross-namespace dependency ✅
- **Root cause**: `extractRequires` didn't find `ns` inside `(do ...)` wrapper from test runner. `resolveNsToPath` only tried `.clj` extension, not `.cljc`. Search path didn't include test suite directory.
- **Fix**: Made `extractRequires` recurse into `do` forms, added `.cljc` fallback in `resolveNsToPath`, added test suite auto-detection in search paths, filtered to only inline `def`/`defn` from required files (not macros).
## Roadmap to 100%
### 2. `add_watch` — STM ref functions missing ✅
- **Root cause**: `(ref 10)` emitted as `ref1(cljInt(10))` instead of `cljRef(cljInt(10))`. `dosync`, `ref-set`, `alter` also missing.
- **Fix**: Added runtime name mappings (`ref``cljRef`, `ref-set``cljRefSet`, `dosync``cljDosync`, `alter``cljAlter`). Implemented minimal STM ref runtime functions.
### Phase A: Closure Scoping Fix (fixes add_watch + remove_watch)
- **Approach**: Instead of wrapping non-last forms in `discard ((proc(): CljVal = ...)())`, emit them as direct statements. The IIFE wrapping was added to handle multi-line expressions as non-last forms, but it breaks variable scoping.
- **Alternative**: Use Nim's `{.inline.}` procs or `block:` labels instead of IIFEs for scope isolation.
- **Impact**: +2 tests (232/233)
### 3. `remove_watch` — Same STM ref issue ✅
- **Root cause**: Same as `add_watch` — uses `ref`, `dosync`, `alter`.
- **Fix**: Same as `add_watch`.
### Phase B: Multi-file Compilation (fixes not_eq)
- **Approach**: When `ns :require` encounters a namespace, find and compile the corresponding `.cljc`/`.clj` file, inline its top-level defs into the current compilation unit.
- **Key files**: `src/cljnim.nim` (file resolution), `src/emitter.nim` (ns handler)
- **Impact**: +1 test (233/233)
## Symbol Emit Fix (Phase C) — Deferred
### Phase C: Symbol Emit Fix (fixes 2 unit tests)
- **Approach**: When emitting a bare symbol that's not a local var and not a runtime function, check if it was defined as a `def`/`defn` in the current compilation unit. If so, emit the mangled name directly instead of `cljSymbol(...)`.
- **Impact**: +2 unit tests (80/80)
The 2 failing unit tests (`emit symbol`, `emit mangled symbol`) require a proper def registry to distinguish between code references (emit mangled name) and symbol values (emit `cljSymbol(...)`). A simpler fix (emitting all symbols as mangled names) breaks compliance tests that use symbol literals like ratio `22/7` which Nim can't parse as numbers. This requires more careful design and will be addressed later.
## Session Log
### 2026-05-11 (current session) — 233/233 (100%)
- Emitter: added `ref`, `ref-set`, `dosync`, `alter` runtime mappings + variadic lists
- Runtime: implemented `cljRef`, `cljRefSet`, `cljDosync`, `cljAlter`
- Reader: added special float token handling for `nan`/`inf`/`-inf`
- Compiler: `extractRequires` now walks `do` forms for `ns` extraction
- Compiler: `resolveNsToPath` tries `.cljc` fallback extension
- Compiler: auto-detects `/tmp/clojure-test-suite/test` as search path
- Compiler: filters required file inlining to only `def`/`defn`/`defn-` (excludes macros)
- Emitter: fixed `NaN`/`Inf` float constants (Nim's `$NaN` produces lowercase `nan`)
- Result: 230/233 → 233/233 compliance
### 2026-05-11 (late session) — Bulk fixes
- Reader: prefer `:default` over `:clj` in conditionals
- Emitter: `cond`, `definterface`, `new` special forms