fix: cond flat-pairs + multi-arity fn/defmethod &rest + self-hosting runtime regen#15
Merged
Merged
Conversation
…e into elisp-cond `analyze-cond` auto-detected "Elisp-style pre-grouped" clauses via `(every? seq? clauses)`. A flat Clojure `cond` whose tests AND results were all lists — e.g. `(cond (hash-table-p m) (maphash ...) (listp m) (dolist ...))` (clel-merge, clel-rename-keys) — was mis-read as pre-grouped and mis-compiled into spurious `(progn)` clauses with broken pairing. Clojure `cond` is ALWAYS flat test/expr pairs; there is no pre-grouped form. Fix: `analyze-cond` now always `(partition 2 clauses)` — no shape inspection. The Elisp pre-grouped shape `(test body...)` is now desugared to flat cond by the `elisp-cond` macro (`elisp-cond-expand`), which remains the ONLY surface that accepts it (single body -> expr, multi-body -> (do ...), empty -> test). elisp-cond stays a registered built-in macro (regression preserved). Verified via nREPL: `(cond (pred x) (f) (pred2 y) (g))` and runtime-shaped nested cond now pair correctly; flat/:else/atom regressions and all three elisp-cond shapes unchanged. Added regression `cond-list-test-and-result`. 541 tests, 0 failures. Kanban: 20260710133453-372031e6 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two anonymous-fn / defmethod param-emission bugs (separate path from defn):
(1) Multi-arity fn literal `(fn ([x] a) ([x y] b))` silently dropped all but
the first arity: analyze-fn took `(first fdecl)` and ignored the rest.
Now analyze-fn detects 2+ arity clauses and produces a multi-arity :fn AST;
emit-node :fn emits `(lambda (&rest clel--args) (cl-case (length clel--args)
...))`, reusing the arity-dispatch codegen shared with multi-arity defn.
(2) defmethod variadic emitted a bare `&` (invalid Elisp) instead of `&rest`:
params went through mangle-name, which doesn't translate the rest marker.
Refactor (DRY): extract `analyze-arity-clauses` (analyzer) and
`emit-arity-cl-case` + `mangle-param` (emitter), now shared by defn and fn.
Single-arity fn and one-wrapped-arity `(fn ([x] x))` stay plain lambdas
(count-1 guard). Multi-arity fn params are not destructured (matches defn).
Verified in Emacs: multi-arity fn dispatches (1->10, 2->15), variadic fn
-> (1 (2 3)), variadic defmethod -> (:k (1 2 3)). Added regression
`multi-arity-fn-and-defmethod-rest`. 542 tests, 0 failures.
Kanban: 20260710134255-3ce11ce0
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ource
The committed clojure-elisp-runtime.el was hand-maintained and could NOT be
regenerated: a naive `compile-runtime` shipped a broken runtime.
Root causes, all fixed in runtime.cljel source (no compiler changes):
- `nth`/`last` in core-fn-mapping rewrite to the very `clel-nth`/`clel-last`
wrappers the runtime defines → self-recursion (clel-last) and wrong arg
order (clel-deref etc. use Elisp index-first `(nth 1 atom)`). Fixed by
routing the 11 `nth` + 1 `last` primitive call sites through the existing
`elisp/`-escape hatch (`(elisp/nth 1 atom)` -> raw `(nth 1 atom)`), which
bypasses core-fn-mapping.
- `clel-nth` was defined ONLY in the hand-edited .el. Added its definition to
runtime.cljel (uses raw `elt`, which is unmapped).
- The Elisp-2 bridge defvars `clojure-core-vector`/`clojure-core-list` existed
only in the .el. Added them to source.
The regenerated .el is now produced entirely from source and is a net
improvement: it also inherits the cond + variadic-`&rest` emitter fixes, so ~24
transducer/utility lambdas that the hand-edited .el still shipped with the bare
`&` bug (e.g. `(lambda (& args) (pcase (length args) ...))`) are now correct
`&rest`, plus `('_ ...)`->`(_ ...)` in clel-range and `cl-min`->`min`.
Verified: regenerated .el byte-compiles with no real warnings and passes a
27-case Emacs functional battery (nth/last/atoms/watchers/lazy/reduce/map/
filter/get/merge/str/range/ALL transducers/constantly/complement/juxt/comp).
Added `runtime-regen-test` locking the self-hosting-safe invariants. 543 tests,
0 failures.
Also synced resources/clojure-elisp/VERSION 0.5.4 -> 0.5.5 (the classpath
version resource that drives the .el header had drifted from the top-level
VERSION; the release process only bumps the latter — filed as follow-up).
Kanban: 20260710133513-7938b464
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
resources/clojure-elisp/VERSION (the classpath resource that stamps the runtime .el MELPA header via core/read-version) drifted from /VERSION (the jar/tag source) because releases bumped only the latter. Add a build sync-version step (also run inside `uber`) that regenerates the resource from /VERSION, plus a test asserting the two files stay in sync. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Untracked test-suite files present in the working tree before this session; checkpointed so the local suite is reproducible. Golden snapshots bless current emitter output as the baseline. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three correctness fixes found during the args-collision audit, each a separate commit with nREPL/Emacs-verified regression tests. All work via carto CRUD + nREPL(7922)/Emacs verification.
Fix 1 —
condis always flat pairs (kanban20260710133453-372031e6)analyze-condguessed "Elisp pre-grouped" via(every? seq? clauses); a flat Clojurecondwhose tests and results were all lists (e.g.clel-merge) mis-compiled into spurious(progn)clauses. Nowcondis always(partition 2 …); the Elisp pre-grouped shape is desugared to flat by theelisp-condmacro (preserved).Fix 2 — multi-arity
fndispatch +defmethodvariadic&rest(kanban20260710134255-3ce11ce0)Multi-arity
fnliterals dropped all but the first arity; now they emit(lambda (&rest clel--args) (cl-case (length clel--args) …)), reusing the multi-arity-defn codegen.defmethodvariadic emitted a bare&; now&rest. DRY: extractedanalyze-arity-clauses,emit-arity-cl-case,mangle-param.Fix 3 — self-hosting runtime is cleanly regenerable (kanban
20260710133513-7938b464)The runtime
.elwas hand-maintained and un-regenerable (nth/lastremapped to theclel-*wrappers the runtime defines → self-recursion/wrong arg order;clel-nth+ Elisp-2 bridges lived only in the.el). Fixed inruntime.cljelsource: routed the 12 primitive call sites through the existingelisp/-escape hatch, addedclel-nth(rawelt) + the bridge defvars. The regenerated.elalso inherits Fixes 1&2, correcting ~24 transducer/utility lambdas the hand-edited.elstill shipped with the bare-&bug, plus('_ …)→(_ …)andcl-min→min.Verification
(1 (2 3)), defmethod(:k (1 2 3)).runtime-regen-test). 543 tests, 2783 assertions, 0 failures.Also synced drifted
resources/clojure-elisp/VERSION(0.5.4→0.5.5); release-pipeline dual-VERSION drift filed as20260710145203-7196a846(P-low).🤖 Generated with Claude Code