Commit graph

9 commits

Author SHA1 Message Date
Yogthos
297d92fbb8 core: fix jolt-r81 at root — move lazy-seq/lazy-cat to the early syntax tier
Root cause: lazy-seq/lazy-cat were defined in 30-macros, which loads AFTER the
seq/coll tiers (10-seq, 20-coll) that use them. In compile mode a tier's forms
are compiled as the tier loads, so (lazy-seq …) in those tiers was compiled when
lazy-seq was not yet a registered macro — i.e. as a CALL to the macro-as-function,
which at runtime returns its own expansion `(make-lazy-seq (fn* [] …))` as data.
That leaked form then flowed into ops like `odd?` (partition-by) → type errors,
or silently produced wrong structure. Interpret/self-host masked it (expand at
call time); the eager fallbacks and the earlier letfn versions masked it by
falling back to the interpreter.

Fix: define lazy-seq/lazy-cat in 00-syntax (loaded first), exactly as when-let
already is for the same reason. They use only seed fns (make-lazy-seq/coll->cells/
concat) + map. With the macro registered early, the seq/coll tiers compile
(lazy-seq …) correctly.

With the root fixed, interleave/reductions/tree-seq drop their letfn workarounds
and use the canonical recursive Clojure forms (top-level / fn-self-name recursion
inside lazy-seq), verified leak-free in compile mode with strict probes.

Regression guards added: partition-by with odd? (the strict pred that exposed the
leak; the prior case used identity which masked it), reductions over an infinite
range, tree-seq summed through a strict filter — all ×3 modes.

Gate: conformance 249x3, lazy-infinite 40/40, fixpoint, self-host, specs+unit green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 17:47:52 -04:00
Yogthos
fa36488dad core: Phase 5 Option A — remaining lazy transformers + nth falsy-element fix
Converts the rest of the lazy transformers to return a LazySeq even over a
concrete collection (dropping the eager "preserve representation" branch, which
returned a vector over vector input): drop, distinct, partition, partition-all,
map-indexed, keep, keep-indexed, take-nth, interpose. Each collapses to its
existing lazy branch run over (lazy-from coll); transducer arities unchanged.

Fixes a latent nth bug exposed by this: core-nth's lazy branch walked with
(ls-first cur) truthiness as the end-of-seq test, so a legitimate false/nil
element was mistaken for the end — (nth (map identity [false 1 2]) 0) threw
instead of returning false, and cond-> (whose macro nths over a now-lazy
(drop 2 clauses) containing the boolean clause tests) failed. nth now walks
with seq-done? and reads via core-first.

Gate: conformance 246x3 (+7 cases), lazy-infinite 18/18, fixpoint, self-host,
all specs+unit green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 14:22:26 -04:00
Yogthos
074eb2da41 core: Phase 5 Option A — map/filter/take/take-while always return a lazy seq
Lazy transformers now return a LazySeq even over a concrete vector, matching
Clojure: (seq? (map inc [1 2 3])) is true, (vector? ...) false. Replaces the
"preserve representation" eager branch (which returned a vector over vector
input) by routing concrete colls through lazy-from + the lazy step machinery.

Flipping these surfaced four boundary bugs, all fixed here:

- cons over a lazy-seq returned a raw cell @[x thunk]; a cons-of-a-cons then
  treated it as a plain 2-array and leaked the rest-thunk as an element (broke
  interleave). cons over lazy now returns a proper LazySeq.
- coll->cells mistook a user vector whose 2nd elem is a function ([first last]
  from juxt) for a cons cell. Cons cells are mutable arrays; user data is
  immutable — route pvec/plist/tuple through immutable tuples and apply the
  [val,fn] cell heuristic only to mutable arrays. Also coerce set/map/string/
  buffer via core-seq.
- ~@ splice over a lazy map result iterated a LazySeq as a Janet table (broke
  lazy-cat / self-ref fib). syntax-quote* now realizes via d-realize before
  splicing; core-sqcat (self-host) already realized.
- core-next did (length r) on a lazy rest (never 0 on a table) and ls-rest
  could return nil → (length nil) crash. core-rest never returns nil; core-next
  uses seq-done? (realizes one cell). seq-done? moved above core-rest.

normalize-pvecs (test helper) realizes lazy-seqs so Janet-= comparisons work.

Gate: conformance 239x3 (interpret/compile/self-host, +10 Option A cases),
lazy-infinite 18/18, fixpoint, self-host, all specs+unit green. (sci-bootstrap
and clojure-test-suite skip — vendored dirs absent in this checkout.)

Remaining for full Option A consistency (jolt-7w4): drop/map-indexed/keep/
keep-indexed/take-nth/interpose/distinct/partition/partition-all still eager
over concrete input.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 14:22:20 -04:00
Yogthos
fcdf0ff535 core: Phase 4 — move reduce-kv to the overlay, fixing the vector case
reduce-kv is pure over reduce/keys/get/nth, so it moves to 20-coll with no
host surface. Both branches fold through reduce, which fixes two latent bugs
in the Janet version: on a vector it saw the pvec as a table and folded over
its internal keys instead of the indices, and it ignored reduced entirely.
nil folds to init. Added vector-index, reduced, and nil spec cases plus a
3-mode conformance case for the vector fix.
2026-06-07 20:05:25 -04:00
Yogthos
e646af123a test: regression coverage for the core migration + the two bug fixes
Spec suites (interpret) for the overlay-migrated fns and both fixes:
  - control-flow: if-let/when-let/if-some/when-some else-scope (9 cases)
  - predicates: not-any?/idents/numeric preds/NaN?/abs/object?/... (24 cases)
  - sequences: nthrest()/nthnext/distinct? value-eq/replace nil/take-last/... (23)
Plus 10 cases added to the all-3-modes conformance set for the highest-risk
fixes (if-let scope, nthrest (), distinct? value-eq), so compile + self-host are
guarded too: conformance 218 -> 228 in all three modes.

Full suite green.
2026-06-06 23:51:13 -04:00
Yogthos
34b880f0d6 conformance: guard the self-hosted pipeline too (218/218 in all three modes)
conformance-test now runs every case under interpret, the bootstrap compiler, AND
the self-hosted pipeline (portable Clojure analyzer -> IR -> Janet back end). All
three pass 218/218, so the self-hosted compiler can't silently regress in CI.
2026-06-06 10:29:36 -04:00
Yogthos
6297a65617 compiler: fix compile-mode correctness (conformance 87->218/218)
Running the conformance suite under compile mode surfaced many forms that
silently miscompiled (the hybrid fallback only catches compile-time errors,
not wrong results). Fixes:

- Global var resolution now mirrors the interpreter's resolve-var: current ns
  (which holds refers) then clojure.core, instead of interning an empty var in
  the current ns. This was the big one — every core fn not in core-renames
  (iterate, update, subvec, reductions, ...) derefed to nil from a user ns.
- Map literals evaluate their keys and values (were emitted as quoted data);
  build via build-map-literal, mirroring the interpreter (struct, or phm when a
  key is a collection).
- Vector literals build a mode-appropriate jolt vector via make-vec (pvec when
  immutable, array when mutable) instead of a bare Janet tuple, so compiled and
  interpreted vectors share one representation (type-strict ops like rseq
  rejected tuples).
- Core fn values resolve dynamically from the runtime env instead of a
  hand-maintained table that had drifted: core-apply mapped to Janet's native
  apply (rejects pvec tails), core-some mapped to core-some?. Removed the table
  and the bogus some rename.
- analyze-form throws uncompilable on interpreter special forms it doesn't
  implement and on definitional/host macros (deftype, defprotocol, reify,
  binding, letfn, read-string, regex/tagged literals, ...), so they fall back
  to the interpreter instead of miscompiling — including nested in compiled
  forms.

conformance-test.janet now runs every case under both interpreter and compiler
so compile-mode correctness is guarded in CI.
2026-06-06 02:41:18 -04:00
Yogthos
360b23c8af feat: distinguish map entries from vectors; min-key NaN ordering; subvec float coercion
- A map entry is a 2-element tuple (Jolt produces tuples only from map iteration;
  vector literals are pvecs, lists are arrays). key/val/map-entry? now accept a
  2-tuple and reject a plain vector, matching Clojure's MapEntry-vs-vector
  distinction — no metadata needed, the representations already differ.
- min-key/max-key reproduce Clojure's NaN-aware folding (2-arg strict </>, then
  <=/>=) and require numeric keys (NaN allowed, strings throw).
- subvec coerces float/NaN indices like (int ...) (truncate, NaN->0) then
  bounds-checks, instead of throwing on non-integers.

min_key 35/14 -> 49/0 (clean); key/val recover the 2-vector cases; subvec floats
fixed. clojure-test-suite pass 3898->3921. Updated conformance-test (key/val now
needs a real entry). spec: map/map-entry-&-key-ordering (14). jpm test green.
2026-06-05 14:22:06 -04:00
Yogthos
16428179fa test: restructure into unit / integration / spec layers + shared harness
Reorganize the flat 49-file test/ into three layers (jpm test recurses, so all
are still discovered):
- test/unit/        white-box component tests (reader, evaluator, types,
                    persistent-map, lazy-seq, macro, interop, compiler)
- test/integration/ cross-cutting + regression batteries (conformance, jank,
                    sci-bootstrap/runtime, features, systematic-coverage, api,
                    core, namespaces, ported clojure suites) and
   .../ports/       ported clojure/cljs test batches pending consolidation
- test/spec/        the behavioral contract (built out in following commits)
- test/support/harness.janet  shared defspec table runner (cases compared via
                    Jolt's own =, with a :throws sentinel) + expect= helpers

Files moved with git mv (history preserved) and import paths fixed for depth.
jpm test green. README Test section updated.

Next: build out test/spec/ to cover the public API area-by-area, mining the
integration batteries and filling gaps.
2026-06-05 00:00:16 -04:00
Renamed from test/conformance.janet (Browse further)