feat: complete atom implementation, new macros, predicates, and test coverage

## Runtime (src/jolt/core.janet, +637/-99)

### Atom system (complete)
- core-atom: :validator and :meta options, watches table, atom-validate/atom-notify-watches helpers
- New functions: swap-vals!, reset-vals!, compare-and-set!, set-validator!, get-validator, add-watch, remove-watch
- Validator: blocks invalid reset!/swap!; watches: notification with old/new values; metadata support
- All 7 functions registered in core-bindings

### New predicates
- integer?, boolean?, list? — all registered in core-bindings

### New math
- abs, rand, rand-int — registered in core-bindings

### Control flow macros (7 new)
- cond, case (with one-of-many list support), condp, dotimes, while, if-not, when-first
- Registered as macros: cond, case, for, dotimes, while, condp, if-not, when-first

### Threading macros (7)
- -> (thread-first), ->> (thread-last), some->, some->>, cond->, cond->>, as->
- Renamed to core-thread-first/core-thread-last to avoid collision with comparison operators

### for comprehension
- Full Clojure for macro: :when, :let modifiers, nested bindings
- Uses map/mapcat expansion, correct last-group optimization

### Sequence operations (15 new)
- last, butlast, second, ffirst, fnext, nfirst, nnext, nthnext, nthrest
- drop-last, take-nth, map-indexed, keep, keep-indexed, reductions

### Nil-safety fixes
- core-reverse, core-sort, core-sort-by, core-distinct now handle nil input

## Tests (7 new files, 361 assertions across all tests)

### Ported from Clojure test suite (4 files)
- clojure-logic-test.janet: 82 assertions (if/and/or/not/some?/nil-punning)
- clojure-control-test.janet: 56 assertions (do/when/if-let/cond/dotimes/while/loop/case)
- clojure-macros-test.janet: 21 assertions (->/->>/some->/some->>/cond->/cond->>/as->)
- clojure-for-test.janet: 4 assertions (:when/:let/nested for)

### Systematic coverage
- clojure-atom-test.janet: 31 assertions (creation/reset!/swap!/swap-vals!/reset-vals!/CAS/validator/watches/meta)
- systematic-coverage.janet: 120+ assertions (predicates/math/comparison/logic/collections/seq-ops/destructuring)
- logic-test.janet: earlier ported logic tests

### Existing tests updated
- lazy-test.janet, phase7-test.janet: updated for nil-safe core functions

## Result
47 test files, all passing. 19 new functions/macros implemented.
Clojure atom semantics complete: validators, watches, metadata, CAS, swap-vals!/reset-vals!.
This commit is contained in:
Yogthos 2026-06-04 12:01:58 -04:00
parent 82bce033a5
commit e2e7fa1447
12 changed files with 1462 additions and 98 deletions

View file

@ -0,0 +1,151 @@
# Ported from clojure/test_clojure/control.clj
(use ../src/jolt/api)
(defn ct-eval [ctx s] (eval-string ctx s))
(print "Ported Control Tests")
# --- test-do ---
(print "test-do...")
(let [ctx (init)]
(assert (= nil (ct-eval ctx "(do)")) "do empty -> nil")
(assert (= 1 (ct-eval ctx "(do 1)")) "do returns last")
(assert (= 2 (ct-eval ctx "(do 1 2)")) "do returns last")
(assert (= 5 (ct-eval ctx "(do 1 2 3 4 5)")) "do returns last"))
(print " ok")
# --- test-loop ---
(print "test-loop...")
(let [ctx (init)]
(assert (= 1 (ct-eval ctx "(loop [] 1)")) "loop body")
(assert (= 3 (ct-eval ctx "(loop [a 1] (if (< a 3) (recur (inc a)) a))")) "loop recur")
(assert (= true (ct-eval ctx
"(= [6 4 2] (loop [a () b [1 2 3]]
(if (seq b)
(recur (conj a (* 2 (first b))) (next b))
a)))")) "loop accum list")
(assert (= true (ct-eval ctx
"(= [2 4 6] (loop [a [] b [1 2 3]]
(if (seq b)
(recur (conj a (* 2 (first b))) (next b))
a)))")) "loop accum vector"))
(print " ok")
# --- test-when ---
(print "test-when...")
(let [ctx (init)]
(assert (= 1 (ct-eval ctx "(when true 1)")) "when true")
(assert (= nil (ct-eval ctx "(when true)")) "when true no body")
(assert (= nil (ct-eval ctx "(when false)")) "when false")
(assert (= nil (ct-eval ctx "(when false 1)")) "when false with body"))
(print " ok")
# --- test-when-not ---
(print "test-when-not...")
(let [ctx (init)]
(assert (= 1 (ct-eval ctx "(when-not false 1)")) "when-not false")
(assert (= nil (ct-eval ctx "(when-not true)")) "when-not true no body")
(assert (= nil (ct-eval ctx "(when-not false)")) "when-not false no body")
(assert (= nil (ct-eval ctx "(when-not true 1)")) "when-not true with body"))
(print " ok")
# --- test-if-not ---
(print "test-if-not...")
(let [ctx (init)]
(assert (= 1 (ct-eval ctx "(if-not false 1)")) "if-not false")
(assert (= 1 (ct-eval ctx "(if-not false 1 2)")) "if-not false with else")
(assert (= nil (ct-eval ctx "(if-not true 1)")) "if-not true")
(assert (= 2 (ct-eval ctx "(if-not true 1 2)")) "if-not true with else"))
(print " ok")
# --- test-when-let ---
(print "test-when-let...")
(let [ctx (init)]
(assert (= 1 (ct-eval ctx "(when-let [a 1] a)")) "when-let simple")
(assert (= 2 (ct-eval ctx "(when-let [[a b] '(1 2)] b)")) "when-let destructure")
(assert (= nil (ct-eval ctx "(when-let [a false] 1)")) "when-let false"))
(print " ok")
# --- test-if-let ---
(print "test-if-let...")
(let [ctx (init)]
(assert (= 1 (ct-eval ctx "(if-let [a 1] a)")) "if-let simple")
(assert (= 2 (ct-eval ctx "(if-let [[a b] '(1 2)] b)")) "if-let destructure")
(assert (= nil (ct-eval ctx "(if-let [a false] 1)")) "if-let false")
(assert (= 1 (ct-eval ctx "(if-let [a false] a 1)")) "if-let false with else"))
(print " ok")
# --- test-if-some ---
(print "test-if-some...")
(let [ctx (init)]
(assert (= 1 (ct-eval ctx "(if-some [a 1] a)")) "if-some simple")
(assert (= false (ct-eval ctx "(if-some [a false] a)")) "if-some false is some")
(assert (= nil (ct-eval ctx "(if-some [a nil] 1)")) "if-some nil")
(assert (= 3 (ct-eval ctx "(if-some [[a b] [1 2]] (+ a b))")) "if-some destructure"))
(print " ok")
# --- test-when-some ---
(print "test-when-some...")
(let [ctx (init)]
(assert (= 1 (ct-eval ctx "(when-some [a 1] a)")) "when-some simple")
(assert (= 2 (ct-eval ctx "(when-some [[a b] [1 2]] b)")) "when-some destructure")
(assert (= false (ct-eval ctx "(when-some [a false] a)")) "when-some false is some")
(assert (= nil (ct-eval ctx "(when-some [a nil] 1)")) "when-some nil"))
(print " ok")
# --- test-cond ---
(print "test-cond...")
(let [ctx (init)]
(assert (= nil (ct-eval ctx "(cond)")) "cond empty -> nil")
(assert (= nil (ct-eval ctx "(cond nil true)")) "cond nil -> nil")
(assert (= nil (ct-eval ctx "(cond false true)")) "cond false -> nil")
(assert (= 1 (ct-eval ctx "(cond true 1)")) "cond true -> 1")
(assert (= 3 (ct-eval ctx "(cond nil 1 false 2 true 3 true 4)")) "cond third branch")
(assert (= :b (ct-eval ctx "(cond false :a true :b)")) "cond skips false")
(assert (= :a (ct-eval ctx "(cond true :a true :b)")) "cond takes first true"))
(print " ok")
# --- test-condp ---
(print "test-condp...")
(let [ctx (init)]
(assert (= :pass (ct-eval ctx "(condp = 1 1 :pass 2 :fail)")) "condp match first")
(assert (= :pass (ct-eval ctx "(condp = 1 2 :fail 1 :pass)")) "condp match second")
(assert (= :pass (ct-eval ctx "(condp = 1 2 :fail :pass)")) "condp default"))
(print " ok")
# --- test-dotimes ---
(print "test-dotimes...")
(let [ctx (init)]
(assert (= nil (ct-eval ctx "(dotimes [n 1] n)")) "dotimes returns nil")
(assert (= 3 (ct-eval ctx
"(let [a (atom 0)]
(dotimes [n 3] (swap! a inc))
@a)")) "dotimes 3 iterations")
(assert (= [0 1 2] (ct-eval ctx
"(let [a (atom [])]
(dotimes [n 3] (swap! a conj n))
@a)")) "dotimes with index"))
(print " ok")
# --- test-while ---
(print "test-while...")
(let [ctx (init)]
(assert (= nil (ct-eval ctx "(while nil 1)")) "while nil returns nil")
(assert (= true (ct-eval ctx
"(= [0 nil]
(let [a (atom 3)
w (while (pos? @a) (swap! a dec))]
[@a w]))")) "while dec to 0"))
(print " ok")
# --- test-case ---
(print "test-case...")
(let [ctx (init)]
(assert (= :number (ct-eval ctx "(case 1 1 :number :default)")) "case match 1")
(assert (= :string (ct-eval ctx "(case \"foo\" \"foo\" :string :default)")) "case match string")
(assert (= :kw (ct-eval ctx "(case :zap :zap :kw :default)")) "case match keyword")
(assert (= :symbol (ct-eval ctx "(case 'pow pow :symbol :default)")) "case match symbol")
(assert (= :default (ct-eval ctx "(case 99 1 :number :default)")) "case default")
(assert (= :matched (ct-eval ctx "(case 2 (2 3 4) :matched :default)")) "case one-of-many"))
(print " ok")
(print "\nAll Ported Control tests passed!")