Commit graph

35 commits

Author SHA1 Message Date
Yogthos
ace1dcd124 chore: close strictness issue jolt-lko 2026-06-05 14:07:56 -04:00
Yogthos
07d5b43fbb feat(strictness): numeric ops reject non-numbers/non-integers like Clojure
- zero?/pos?/neg? throw on non-numbers; odd?/even? throw on non-integers
  (nil, infinities, NaN, fractional) via need-num/need-int helpers
- comparisons < > <= >= throw on non-number args (1-arity stays true, no check)
- max/min throw on non-number args
- quot/rem/mod throw on zero divisor and non-finite operands

odd?/even?/lt/gt/lt_eq/gt_eq suite files now clean; pass 3691->3738.
Updated systematic-coverage-test (zero? nil now throws). spec:
numbers/strictness (15). jpm test green.
2026-06-05 10:58:21 -04:00
Yogthos
20ab88dd0c chore: beads sync for follow-up fixes 2026-06-05 09:36:08 -04:00
Yogthos
03652dce5d feat: ##Inf/##-Inf/##NaN literals, infinite?/NaN?, fix intval? for infinity
The reader now reads the symbolic float values ##Inf, ##-Inf and ##NaN. Added
infinite? and NaN? predicates. Fixed intval? to exclude infinity (floor(inf)=inf
but inf isn't integer-valued), so float?/double? are true for ##Inf and
int?/pos-int?/nat-int?/neg-int? are false for it.

This unblocked many number-test files whose  forms previously failed to
READ (##Inf/##NaN literals), so clojure-test-suite jumped from 2241 to 2539
assertions and pass 1719 -> 1955. Baseline raised to 1900. NaN_qmark now runs.

float?/double? on integer-valued doubles (1.0) remain false: Janet represents
an integer and an integer-valued double identically, so they're inherently
indistinguishable — documented in the README Numbers section.

spec: numbers/floats-&-symbolic-values (15 cases). jpm test green.
Closes jolt-fy8 (fixable parts; int-vs-float ambiguity is a documented divergence).
2026-06-05 09:35:44 -04:00
Yogthos
09532dac05 fix: transient invokable lookup, assoc! odd args, use-after-persistent! invalidation
Real transient correctness gaps surfaced by the clojure-test-suite:

- Transients are now invokable for read-only lookup like their persistent forms:
  ((transient v) i), ((transient m) k [default]), (:k (transient m)),
  ((transient s) x). jolt-invoke/coll-lookup gained a transient branch
  (transient-lookup) that indexes :arr / canon-keyed :tbl. Added phm/canon as a
  public canonicalizer so collection keys compare by value here too.
- assoc! accepts an ODD arg count (a missing final value is nil), unlike assoc —
  core-assoc! now uses nil-safe get for the value instead of erroring.
- Using a transient after persistent! (or a second persistent!, or pop! on an
  empty transient vector) now throws, via a :jolt/persistent invalidation flag
  checked by the mutating ops. Catches the classic transient footgun.

spec: transients-spec gains invokable-lookup (7), assoc!-odd-args (4),
invalidation (4). clojure-test-suite pass 1704->1719.

Remaining transient fails are accepted lenient divergences (jolt doesn't throw
on bad-shape conj!/assoc!/pop! of wrong-typed args; nil set elements). jpm test green.
2026-06-05 09:29:36 -04:00
Yogthos
e75771084e fix: merge nil/empty + conj semantics to match Clojure
core-merge returned {} for (merge) and errored on nil args. Rewrite to follow
Clojure: (when (some identity maps) (reduce conj (or (first maps) {}) (rest maps))).

- (merge) and (merge nil nil) -> nil
- nil args elsewhere are no-ops: (merge {} nil) -> {}, (merge nil {:a 1}) -> {:a 1}
- later args follow conj semantics: a map merges its entries, a [k v]
  vector/map-entry adds that entry ((merge {} [:foo 1]) -> {:foo 1},
  (merge {} (first {:a 1})) -> {:a 1})
- collection keys preserved (first arg's phm type kept; entries via core-assoc
  which promotes struct->phm for collection keys)

spec: 9 new merge cases in maps-spec. merge.cljc 13/18/11 -> 29/11/2.
clojure-test-suite pass 1688->1704. jpm test green.

Remaining merge.cljc fails are the lenient-where-Clojure-throws class (atomic
args, >2-element vectors). Fixes jolt-dxx.
2026-06-05 09:22:27 -04:00
Yogthos
2885650ef6 fix: transducers/reduce short-circuit over infinite seqs via reduced
transduce-reduce and core-reduce both called realize-for-iteration, fully
realizing the coll before the reduce loop — so an infinite lazy seq hung before
the reduced short-circuit could fire. (into [] (take 5) (range)) etc. never
terminated.

Add reduce-with-reduced, which steps a lazy seq one cell at a time
(realize-ls/ls cell protocol), checking reduced? after each element so a take/
take-while transducer (or any reducing fn returning reduced) terminates over an
infinite seq. Route transduce-reduce and both core-reduce arities through it;
2-arg reduce now seeds from the first element and reduces the rest lazily.

spec: transducers/short-circuit + reduce/honors-reduced (13 cases).
clojure-test-suite timeouts 7->6, pass 1683->1688. jpm test green.

Fixes jolt-kxb.
2026-06-05 09:18:35 -04:00
Yogthos
ae2899ba61 chore: beads issue tracking for conformance gaps 2026-06-05 09:05:41 -04:00
Yogthos
0ca678a159 test: port clojure-test-suite as baseline-guarded integration battery
Run the external cross-dialect clojure-test-suite (lread/clojure-test-suite,
240 per-fn .cljc files in clojure.test format) against Jolt:

- test/support/clojure_test.clj: minimal clojure.test + portability shims
  (deftest/is/testing/are + when-var-exists/thrown?/big-int?/lazy-seq?) — just
  enough surface to load the suite and tally pass/fail/error. Pre-loaded so the
  suite's (require [clojure.test ...]) finds it already populated.
- test/integration/suite-worker.janet: one-shot worker that loads the shim,
  the suite's number-range helper ns, and a single .cljc file, then prints
  'pass fail error'.
- test/integration/clojure-test-suite-test.janet: spawns a worker per file
  under an ev/with-deadline wall-clock budget (so infinite-seq tests that hang
  Jolt's eager evaluator are auto-contained, not a manual skip-list) and asserts
  pass/clean-file counts stay at/above a baseline. References ~/src/clojure-test-suite
  if present; skips cleanly when absent, like the jank battery.

Current: 210 files run, 7 timed out, 2233 assertions -> 1683 pass / 350 fail /
200 error, 23 clean files. Remaining fails are genuine divergences (float/ratio/
bigint, lenient transients where Clojure throws), tracked separately.

Fixes two real evaluator bugs the suite surfaced:
- :refer now preserves a referred macro's :macro flag (was interned as a plain
  value, degrading referred macros to functions).
- resolve-var now resolves ns aliases (like resolve-sym), so aliased macros
  (e.g. p/thrown? via :as p) dispatch as macros instead of being called as fns.
2026-06-05 09:04:38 -04:00
Yogthos
e409edf8d9 chore: export beads (jolt-dd5 closed) 2026-06-05 01:24:07 -04:00
Yogthos
a681daf7b9 test: fold cljs ports into spec; add exceptions spec + gap coverage
Mine the cljs port batteries into the spec layer and delete them (their behavior
is covered by spec; the unique functions they exercised are now specced).
Removed test/integration/ports/ entirely.

New/expanded spec coverage from the mining:
- spec/exceptions-spec: try/catch/finally, throw, ex-info/ex-message/ex-data/ex-cause
- doto, pr-str, keyword/symbol constructors, atom?, dynamic var binding

Two rare edges filed (jolt-...): rethrow of a caught ex-info re-wraps it; var-set
on a dynamic var inside binding no-ops. Core try/catch/ex-info and binding work.

Test layout is now spec (22 files, ~677 cases) / integration / unit / support.
conformance 218/218, jpm test green.
2026-06-05 01:12:39 -04:00
Yogthos
3f5b42d784 chore: close jolt-do7 (collection keys) and jolt-e4p (regex PEG limits) 2026-06-05 01:03:05 -04:00
Yogthos
b353d625f1 test(spec): collections contract (sequences, vectors, lists, maps, sets)
Add behavioral spec suites for the collection API (~180 cases). Writing them
surfaced and fixed real bugs:
- (count nil) errored; now 0 (Clojure semantics)
- (repeat x) and (repeatedly f) — the infinite 1-arg arities — were unsupported;
  now return lazy infinite seqs
- set equality used deep= (representation-sensitive), so a set of map literals
  could differ from an equal set built differently; now value-based (each
  element value-equal to some element of the other)

Known limitation filed: phm-typed maps (from hash-map) used as set elements /
map keys hash by identity (map *literals* are structs and work).

jpm test green.
2026-06-05 00:08:37 -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
Yogthos
1898308926 docs: refresh Differences — bigint/+'/transients/byte-arrays now provided
Update stale claims: bigint/biginteger (int/s64), +'/*'/inc' aliases, transients
(correctness-only), and the byte-array-via-buffer / array family are now
implemented. Note clojure.repl/template and JVM reflection/proxy remain out.
2026-06-04 23:48:38 -04:00
Yogthos
4eead1ac0f fix: deftype now registers inline protocol/interface methods
Raw (deftype T [fields] Proto (method [this] body) ...) silently ignored its
protocol body — only the constructor was created, so protocol dispatch on a
deftype instance always failed (No method ... for T). defrecord worked only
because its macro expanded the body into extend-type forms.

deftype now does the same: emit an extend-type per protocol, wrapping each
method body in a let that binds the type's fields from the instance. This is a
real correctness fix (any deftype-with-protocols was broken) and unblocks SCI's
sci.lang.Var (a deftype implementing vars/IVar) — the clojure.core aggregation
map now gets past protocol dispatch.

conformance 218/218, features 78/78, jank 120.
2026-06-04 23:26:25 -04:00
Yogthos
39edb88074 feat: ~40 more core fns + fix map iteration to yield entries
While digging into test-load-sci, add genuinely-missing clojure.core fns:
doall/dorun/run!/tree-seq, key/val/map-entry?, rand-nth/replicate/
bounded-count/counted?/reversible?/seqable?, nat-int?/pos-int?/neg-int?/
double?/float?/ratio?/decimal?/rational?/numerator/denominator, list*,
special-symbol?/record?, promise/deliver, comparator/completing/halt-when/
ensure-reduced/unreduced, keyword-identical?/object?/tagged-literal/re-groups.

Fix a real pre-existing bug: iterating a map (first/seq/map/filter/reduce/vec/
realize-for-iteration) returned keys or values instead of [k v] entries.
Map literals are Janet structs (not phm), which several paths mishandled.
Now first/seq/realize-for-iteration yield entries for both structs and phms,
matching Clojure — e.g. (vec {:a 1}) => [[:a 1]], (map key m), (reduce f init m).

Tests: conformance 218/218 (+12), features 78/78, jank 120. Advances the SCI
bootstrap further (its clojure.core aggregation map resolves more symbols).
2026-06-04 19:38:25 -04:00
Yogthos
e43f39bc36 feat: add missing core fns (bit-clear, get-method, second, predicates, ...)
Resolve symbols that were unbound or leaking to broken Janet builtins:
- bit-clear/bit-set/bit-flip/bit-test/bit-and-not
- get-method/methods (multimethod introspection)
- second/ffirst/nfirst/fnext/nnext, last (was leaking to Janet, broke on pvec),
  drop-last/take-last
- ident?/simple-symbol?/qualified-keyword?/simple-keyword?/simple-ident?/
  qualified-ident?/inst?/inst-ms/uri?/uuid?/bytes?/tagged-literal?
- clojure.string/re-quote-replacement

Advances the SCI bootstrap; conformance 206/206, features 71/71.
2026-06-04 19:10:45 -04:00
Yogthos
1ca93d61c6 feat: build-time mutable/immutable collection toggle + fix list mutation
Add src/jolt/config.janet exposing a build-time `mutable?` constant read from
JOLT_MUTABLE at compile time (jpm build). Default is immutable.

Fix correctness bug: conj on a list (Janet array) mutated the original in
place. In immutable mode conj now copies first; in mutable mode it mutates.

Round 1 of persistent-collections work.
2026-06-04 18:15:48 -04:00
Yogthos
1b37e56df4 feat: map literals evaluate their values (fixes critical latent bug); expand SCI bootstrap to 14 files (ctx_store/deftype/records/core_protocols/hierarchies + sci.impl.io stub), 390/392 forms; fix multi-arity defmethod (builds fn*) and variadic macrofy; SCI test loaders tolerant of the 2 redundant clojure.core registration maps 2026-06-04 17:28:44 -04:00
Yogthos
37c8971d41 feat: full regex engine (Path A) — parser->AST->PEG grammar with continuation-passing backtracking + position-based group captures; capturing groups [whole g1...], greedy/lazy backtracking through groups, lookahead, (?i) flag, anchors/\b, $N replace; re-find/matches/seq + string split/replace 2026-06-04 16:24:02 -04:00
Yogthos
7dbccb60c0 feat: compiler-path IFn dispatch via jolt-call (vector/map/set/keyword/record callable in :compile? mode); fix compiled set literals (->make-phs) and char consts 2026-06-04 15:56:36 -04:00
Yogthos
b0cb3afbf2 feat: transducers — map/filter/remove/take/drop/take-while/drop-while/map-indexed return transducers on no-coll arity; transduce/into-xform/sequence/eduction/reduced/unreduced; reduce honors reduced 2026-06-04 15:48:04 -04:00
Yogthos
e1556fd102 docs: PLAN.md + memory updates for conformance gap-closing batch 2026-06-04 15:28:30 -04:00
Yogthos
31d63df133 docs: PLAN.md Phase 19 conformance-hardening summary 2026-06-04 14:58:16 -04:00
Yogthos
044a16d1b2 fix: lazy filter/take-while/remove over lazy input (supports infinite seqs); fix take-while iterating lazy-seq table 2026-06-04 14:56:26 -04:00
Yogthos
77c52553cb fix: REPL lazy-seq printer walks full chain (capped); def/defn return var (prints #'ns/name); fix var-name printer bug 2026-06-04 14:55:11 -04:00
Yogthos
8f97e1d822 feat: require loads stdlib namespaces from disk + aliased-ns resolution (s/join); fix str-join to stringify elements (Clojure semantics) 2026-06-04 14:45:52 -04:00
Yogthos
08de360315 fix: Clojure str/pr-str semantics (nil->'', keywords ':b', symbols, collections render with reader syntax); fix incorrect str-nil test 2026-06-04 14:33:45 -04:00
Yogthos
cbc610c665 fix: unified recursive destructuring (nested seq/map, :strs/:syms, :or, :as, fn-params); proper defn docstring/attr-map handling 2026-06-04 14:31:24 -04:00
Yogthos
92682187a8 fix: truly lazy concat enabling self-referential lazy-cat/lazy-seq (fib-seq); CRITICAL conformance round 28/28 2026-06-04 14:09:15 -04:00
Yogthos
3c77f6a9c0 fix: lazy iterate/range(infinite)/single-coll-map over lazy input; realize lazy-seqs in reduce/filter 2026-06-04 14:06:02 -04:00
Yogthos
2ea5b2bb5f fix: unify vector repr on tuples; realize lazy-seqs in =/vec/into; fix into for maps/lists; restore ns after PV load; deftype field-access fallback in IFn dispatch 2026-06-04 13:59:36 -04:00
Yogthos
accb7a2c2f feat: collection-as-IFn dispatch (vector/map/set/keyword); add Clojure conformance harness 2026-06-04 13:50:54 -04:00
Yogthos
1d47fc9ad2 bd init: initialize beads issue tracking 2026-06-04 13:46:11 -04:00