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:
parent
82bce033a5
commit
e2e7fa1447
12 changed files with 1462 additions and 98 deletions
|
|
@ -31,6 +31,10 @@
|
|||
(defn core-even? [n] (= 0 (% n 2)))
|
||||
(defn core-odd? [n] (not= 0 (% n 2)))
|
||||
|
||||
(defn core-integer? [x] (and (number? x) (= x (math/floor x))))
|
||||
(defn core-boolean? [x] (or (= x true) (= x false)))
|
||||
(defn core-list? [x] (and (array? x) (not (get x :jolt/type))))
|
||||
|
||||
(defn core-empty? [coll]
|
||||
(if (nil? coll) true
|
||||
(if (set? coll) (= 0 (coll :cnt))
|
||||
|
|
@ -74,6 +78,10 @@
|
|||
(defn core-max [& args] (apply max args))
|
||||
(defn core-min [& args] (apply min args))
|
||||
|
||||
(defn core-abs [x] (if (neg? x) (- 0 x) x))
|
||||
(defn core-rand [] (math/random))
|
||||
(defn core-rand-int [n] (math/floor (* (math/random) n)))
|
||||
|
||||
# ============================================================
|
||||
# Comparison
|
||||
# ============================================================
|
||||
|
|
@ -210,11 +218,8 @@
|
|||
(if (= 0 (length r)) nil r)))
|
||||
|
||||
(defn core-cons [x coll]
|
||||
(if (nil? coll)
|
||||
@[x]
|
||||
(if (tuple? coll)
|
||||
(tuple/slice (tuple ;(array/insert (array/slice coll) 0 x)))
|
||||
(array/insert coll 0 x))))
|
||||
"Returns a lazy-seq compatible cons cell [first, rest-thunk]."
|
||||
@[x (fn [] coll)])
|
||||
|
||||
(defn core-seq [coll]
|
||||
(if (or (nil? coll) (and (or (tuple? coll) (array? coll)) (= 0 (length coll))))
|
||||
|
|
@ -284,10 +289,27 @@
|
|||
# Sequence operations
|
||||
# ============================================================
|
||||
|
||||
(defn- realize-for-iteration [c]
|
||||
"If c is a lazy-seq, traverse and return all elements as an array.
|
||||
Otherwise return c as-is. Warning: will loop on infinite lazy-seqs."
|
||||
(if (lazy-seq? c)
|
||||
(do
|
||||
(var items @[])
|
||||
(var cur c)
|
||||
(while (not (nil? (ls-first cur)))
|
||||
(array/push items (ls-first cur))
|
||||
(set cur (ls-rest cur)))
|
||||
items)
|
||||
c))
|
||||
|
||||
(defn core-map [f & colls]
|
||||
(let [first-coll (colls 0)
|
||||
(let [realized (map realize-for-iteration colls)
|
||||
first-coll (realized 0)
|
||||
result (if (= 1 (length colls))
|
||||
(array ;(map f first-coll))
|
||||
(do
|
||||
(var res @[])
|
||||
(each x first-coll (array/push res (f x)))
|
||||
res)
|
||||
(do
|
||||
(var res @[])
|
||||
(var idxs @{})
|
||||
|
|
@ -297,7 +319,7 @@
|
|||
(var args @[])
|
||||
(var i 0)
|
||||
(while (< i (length colls))
|
||||
(let [c (colls i) j (idxs i)]
|
||||
(let [c (realized i) j (idxs i)]
|
||||
(if (>= j (length c))
|
||||
(do (set done true) (break))
|
||||
(array/push args (c j))))
|
||||
|
|
@ -341,19 +363,37 @@
|
|||
(error "Wrong number of args passed to: reduce"))))
|
||||
|
||||
(defn core-take [n coll]
|
||||
(var result @[])
|
||||
(var i 0)
|
||||
(var c (if (lazy-seq? coll) (realize-ls coll) coll))
|
||||
(while (and (< i n) (< i (length c)))
|
||||
(array/push result (c i))
|
||||
(++ i))
|
||||
(if (tuple? c) (tuple/slice (tuple ;result)) result))
|
||||
(if (lazy-seq? coll)
|
||||
(do
|
||||
(var result @[])
|
||||
(var cur coll)
|
||||
(var i 0)
|
||||
(while (and (< i n) (not (nil? (ls-first cur))))
|
||||
(array/push result (ls-first cur))
|
||||
(set cur (ls-rest cur))
|
||||
(++ i))
|
||||
result)
|
||||
(do
|
||||
(var result @[])
|
||||
(var i 0)
|
||||
(while (and (< i n) (< i (length coll)))
|
||||
(array/push result (coll i))
|
||||
(++ i))
|
||||
(if (tuple? coll) (tuple/slice (tuple ;result)) result))))
|
||||
|
||||
(defn core-drop [n coll]
|
||||
(var c (if (lazy-seq? coll) (realize-ls coll) coll))
|
||||
(if (tuple? c)
|
||||
(tuple/slice c (min n (length c)))
|
||||
(array/slice c (min n (length c)))))
|
||||
(if (lazy-seq? coll)
|
||||
(do
|
||||
(var cur coll)
|
||||
(var i 0)
|
||||
(while (and (< i n) (ls-first cur))
|
||||
(set cur (ls-rest cur))
|
||||
(++ i))
|
||||
(if (nil? (ls-first cur)) nil cur))
|
||||
(do
|
||||
(if (tuple? coll)
|
||||
(tuple/slice coll (min n (length coll)))
|
||||
(array/slice coll (min n (length coll)))))))
|
||||
|
||||
(defn core-take-while [pred coll]
|
||||
(var result @[])
|
||||
|
|
@ -370,54 +410,130 @@
|
|||
(tuple/slice c start)
|
||||
(array/slice c start)))
|
||||
|
||||
(defn coll->cells [c]
|
||||
"Convert a seqable to lazy-seq cell chain: nil or [first, rest-thunk].
|
||||
If the value is a function, call it and use the result.
|
||||
If the result is already a cell (array of [val, function]), return it directly."
|
||||
(if (nil? c) nil
|
||||
(if (function? c)
|
||||
(let [r (c)]
|
||||
(if (and (indexed? r) (= 2 (length r)) (function? (in r 1)))
|
||||
r
|
||||
(coll->cells r)))
|
||||
(if (lazy-seq? c) (realize-ls c)
|
||||
(if (indexed? c)
|
||||
(if (= 0 (length c)) nil
|
||||
(let [f (in c 0)
|
||||
rest (if (> (length c) 1)
|
||||
(if (tuple? c) (tuple/slice c 1) (array/slice c 1))
|
||||
nil)]
|
||||
@[f (fn [] (coll->cells rest))]))
|
||||
nil)))))
|
||||
|
||||
(defn core-concat [& colls]
|
||||
(var result @[])
|
||||
(each c colls
|
||||
(def c-realized (if (lazy-seq? c) (realize-ls c) c))
|
||||
(each x c-realized (array/push result x)))
|
||||
result)
|
||||
"Lazy concatenation — returns a lazy-seq that yields elements one at a time.
|
||||
Supports self-referencing sequences like fib-seq."
|
||||
(var cs (if (tuple? colls) (array/slice colls) colls))
|
||||
(var cur nil)
|
||||
(defn next-cell []
|
||||
(var cell (coll->cells cur))
|
||||
(while (and (nil? cell) (not (nil? cs)) (> (length cs) 0))
|
||||
(set cur (in cs 0))
|
||||
(set cs (array/slice cs 1))
|
||||
(set cell (coll->cells cur)))
|
||||
(if (nil? cell) nil
|
||||
@[(in cell 0)
|
||||
(fn []
|
||||
(set cur (in cell 1))
|
||||
(next-cell))]))
|
||||
(make-lazy-seq next-cell))
|
||||
|
||||
(defn core-reverse [coll]
|
||||
(var c (if (lazy-seq? coll) (realize-ls coll) coll))
|
||||
(var result @[])
|
||||
(var i (dec (length c)))
|
||||
(while (>= i 0)
|
||||
(array/push result (c i))
|
||||
(-- i))
|
||||
(if (tuple? c) (tuple/slice (tuple ;result)) result))
|
||||
(if (nil? coll) @[]
|
||||
(if (lazy-seq? coll)
|
||||
(do
|
||||
(var result @[])
|
||||
(var cur coll)
|
||||
(while (not (nil? (ls-first cur)))
|
||||
(array/push result (ls-first cur))
|
||||
(set cur (ls-rest cur)))
|
||||
(var reversed @[])
|
||||
(var i (dec (length result)))
|
||||
(while (>= i 0)
|
||||
(array/push reversed (in result i))
|
||||
(-- i))
|
||||
reversed)
|
||||
(do
|
||||
(var result @[])
|
||||
(var i (dec (length coll)))
|
||||
(while (>= i 0)
|
||||
(array/push result (coll i))
|
||||
(-- i))
|
||||
(if (tuple? coll) (tuple/slice (tuple ;result)) result)))))
|
||||
|
||||
(defn core-nth
|
||||
"Return the nth element of a sequential collection."
|
||||
[coll idx &opt default]
|
||||
(var c (if (lazy-seq? coll) (realize-ls coll) coll))
|
||||
(if (and (>= idx 0) (< idx (length c)))
|
||||
(in c idx)
|
||||
(if (nil? default)
|
||||
(error (string "Index " idx " out of bounds, length: " (length c)))
|
||||
default)))
|
||||
(if (lazy-seq? coll)
|
||||
(do
|
||||
(var cur coll)
|
||||
(var i 0)
|
||||
(while (and (< i idx) (ls-first cur))
|
||||
(set cur (ls-rest cur))
|
||||
(++ i))
|
||||
(if (ls-first cur) (ls-first cur)
|
||||
(if (nil? default)
|
||||
(error (string "Index " idx " out of bounds"))
|
||||
default)))
|
||||
(do
|
||||
(var c (if (lazy-seq? coll) (realize-ls coll) coll))
|
||||
(if (and (>= idx 0) (< idx (length c)))
|
||||
(in c idx)
|
||||
(if (nil? default)
|
||||
(error (string "Index " idx " out of bounds, length: " (length c)))
|
||||
default)))))
|
||||
|
||||
(defn core-sort [coll]
|
||||
(var c (if (lazy-seq? coll) (realize-ls coll) coll))
|
||||
(let [arr (if (tuple? c) (array/slice c) c)
|
||||
sorted (sort arr)]
|
||||
(if (tuple? c) (tuple/slice (tuple ;sorted)) sorted)))
|
||||
(if (nil? coll) @[]
|
||||
(if (lazy-seq? coll)
|
||||
(do
|
||||
(var items @[])
|
||||
(var cur coll)
|
||||
(while (not (nil? (ls-first cur)))
|
||||
(array/push items (ls-first cur))
|
||||
(set cur (ls-rest cur)))
|
||||
(sort items))
|
||||
(let [arr (if (tuple? coll) (array/slice coll) coll)
|
||||
sorted (sort arr)]
|
||||
(if (tuple? coll) (tuple/slice (tuple ;sorted)) sorted)))))
|
||||
|
||||
(defn core-sort-by [keyfn coll]
|
||||
(if (nil? coll) (break @[]))
|
||||
(var c (if (lazy-seq? coll) (realize-ls coll) coll))
|
||||
(let [arr (if (tuple? c) (array/slice c) c)
|
||||
sorted (sort-by keyfn arr)]
|
||||
(if (tuple? c) (tuple/slice (tuple ;sorted)) sorted)))
|
||||
|
||||
(defn core-distinct [coll]
|
||||
(var seen @{})
|
||||
(var result @[])
|
||||
(var c (if (lazy-seq? coll) (realize-ls coll) coll))
|
||||
(each x c
|
||||
(if (nil? (seen x))
|
||||
(do
|
||||
(put seen x true)
|
||||
(array/push result x))))
|
||||
(if (tuple? c) (tuple/slice (tuple ;result)) result))
|
||||
(if (nil? coll) @[]
|
||||
(if (lazy-seq? coll)
|
||||
(do
|
||||
(var seen @{})
|
||||
(var result @[])
|
||||
(var cur coll)
|
||||
(while (not (nil? (ls-first cur)))
|
||||
(let [x (ls-first cur)]
|
||||
(if (nil? (seen x))
|
||||
(do (put seen x true) (array/push result x))))
|
||||
(set cur (ls-rest cur)))
|
||||
result)
|
||||
(do
|
||||
(var seen @{})
|
||||
(var result @[])
|
||||
(each x coll
|
||||
(if (nil? (seen x))
|
||||
(do (put seen x true) (array/push result x))))
|
||||
(if (tuple? coll) (tuple/slice (tuple ;result)) result)))))
|
||||
|
||||
(defn core-group-by [f coll]
|
||||
(var result @{})
|
||||
|
|
@ -590,15 +706,18 @@
|
|||
|
||||
(defn core-lazy-seq [& body]
|
||||
@[{:jolt/type :symbol :ns nil :name "make-lazy-seq"}
|
||||
@[{:jolt/type :symbol :ns nil :name "fn*"} [] ;body]])
|
||||
@[{:jolt/type :symbol :ns nil :name "fn*"} []
|
||||
@[{:jolt/type :symbol :ns nil :name "coll->cells"}
|
||||
@[{:jolt/type :symbol :ns nil :name "do"} ;body]]]])
|
||||
|
||||
(defn core-lazy-cat [& colls]
|
||||
"Macro: (lazy-cat & colls) — concatenate lazy sequences, wrapping each coll in lazy-seq."
|
||||
(def result @[])
|
||||
(array/push result {:jolt/type :symbol :ns nil :name "concat"})
|
||||
"Macro: (lazy-cat & colls) — concatenate lazy sequences, wrapping each coll in lazy-seq.
|
||||
concat is now lazy, so no outer make-lazy-seq wrapping is needed."
|
||||
(def concat-form @[])
|
||||
(array/push concat-form {:jolt/type :symbol :ns nil :name "concat"})
|
||||
(each c colls
|
||||
(array/push result @[{:jolt/type :symbol :ns nil :name "lazy-seq"} c]))
|
||||
result)
|
||||
(array/push concat-form @[{:jolt/type :symbol :ns nil :name "lazy-seq"} c]))
|
||||
concat-form)
|
||||
|
||||
(defn core-set [coll]
|
||||
(apply core-hash-set (if (tuple? coll) (array/slice coll) coll)))
|
||||
|
|
@ -727,31 +846,102 @@
|
|||
# Atom
|
||||
# ============================================================
|
||||
|
||||
(defn core-atom [val]
|
||||
@{:jolt/type :jolt/atom :value val :watches @{}})
|
||||
(defn core-atom
|
||||
"Create an atom. Accepts optional :validator fn and :meta map."
|
||||
[val & opts]
|
||||
(var atm @{:jolt/type :jolt/atom :value val :watches @{} :validator nil})
|
||||
(var i 0)
|
||||
(while (< i (length opts))
|
||||
(case (opts i)
|
||||
:validator (put atm :validator (opts (+ i 1)))
|
||||
:meta (let [m (opts (+ i 1))]
|
||||
(var meta-tab @{})
|
||||
(each k (keys m) (put meta-tab k (get m k)))
|
||||
(table/setproto atm meta-tab)
|
||||
(put atm :jolt/meta m)))
|
||||
(+= i 2))
|
||||
atm)
|
||||
|
||||
(defn core-atom? [x]
|
||||
(and (table? x) (= :jolt/atom (x :jolt/type))))
|
||||
|
||||
(defn core-deref [ref]
|
||||
(cond
|
||||
# Jolt atom
|
||||
(and (table? ref) (= :jolt/atom (ref :jolt/type)))
|
||||
(ref :value)
|
||||
# Jolt var (from types.janet)
|
||||
(and (table? ref) (= :jolt/var (ref :jolt/type)))
|
||||
(ref :root)
|
||||
# default: return as-is
|
||||
ref))
|
||||
|
||||
(defn- atom-validate
|
||||
"Call validator on atm. Returns the value if valid, errors otherwise."
|
||||
[atm val]
|
||||
(let [v (atm :validator)]
|
||||
(if v
|
||||
(if (v val) val
|
||||
(error "Validator rejected value"))
|
||||
val)))
|
||||
|
||||
(defn- atom-notify-watches
|
||||
[atm old-val new-val]
|
||||
(loop [[k w] :pairs (atm :watches)]
|
||||
(w k atm old-val new-val)))
|
||||
|
||||
(defn core-reset! [atm val]
|
||||
(put atm :value val)
|
||||
val)
|
||||
(let [old-val (atm :value)]
|
||||
(atom-validate atm val)
|
||||
(put atm :value val)
|
||||
(atom-notify-watches atm old-val val)
|
||||
val))
|
||||
|
||||
(defn core-swap! [atm f & args]
|
||||
(let [new-val (apply f (atm :value) args)]
|
||||
(put atm :value new-val)
|
||||
new-val))
|
||||
(var old-val (atm :value))
|
||||
(var new-val (apply f old-val args))
|
||||
(atom-validate atm new-val)
|
||||
(put atm :value new-val)
|
||||
(atom-notify-watches atm old-val new-val)
|
||||
new-val)
|
||||
|
||||
(defn core-reset-vals! [atm val]
|
||||
(let [old-val (atm :value)]
|
||||
(atom-validate atm val)
|
||||
(put atm :value val)
|
||||
(atom-notify-watches atm old-val val)
|
||||
[old-val val]))
|
||||
|
||||
(defn core-swap-vals! [atm f & args]
|
||||
(var old-val (atm :value))
|
||||
(var new-val (apply f old-val args))
|
||||
(atom-validate atm new-val)
|
||||
(put atm :value new-val)
|
||||
(atom-notify-watches atm old-val new-val)
|
||||
[old-val new-val])
|
||||
|
||||
(defn core-compare-and-set! [atm old-val new-val]
|
||||
(if (= old-val (atm :value))
|
||||
(do
|
||||
(atom-validate atm new-val)
|
||||
(put atm :value new-val)
|
||||
(atom-notify-watches atm old-val new-val)
|
||||
true)
|
||||
false))
|
||||
|
||||
(defn core-set-validator! [atm validator-fn]
|
||||
(put atm :validator validator-fn)
|
||||
nil)
|
||||
|
||||
(defn core-get-validator [atm]
|
||||
(atm :validator))
|
||||
|
||||
(defn core-add-watch [atm key watch-fn]
|
||||
(let [watches (atm :watches)]
|
||||
(put watches key watch-fn)
|
||||
atm))
|
||||
|
||||
(defn core-remove-watch [atm key]
|
||||
(let [watches (atm :watches)]
|
||||
(put watches key nil)
|
||||
atm))
|
||||
|
||||
# ============================================================
|
||||
# Threading macros (as regular functions? No, as macros in Clojure)
|
||||
|
|
@ -773,6 +963,53 @@
|
|||
(put gensym_counter :val (+ n 1))
|
||||
{:jolt/type :symbol :ns nil :name (string prefix-string n)})
|
||||
|
||||
(defn core-cond
|
||||
"Macro: (cond test1 expr1 test2 expr2 ... :else default)
|
||||
-> (if test1 expr1 (if test2 expr2 ...))"
|
||||
[& clauses]
|
||||
(defn build [cls]
|
||||
(if (= 0 (length cls))
|
||||
nil
|
||||
(let [t (first cls)]
|
||||
(if (= :else t)
|
||||
(if (> (length cls) 1) (in cls 1) nil)
|
||||
(if (< (length cls) 2)
|
||||
(error "cond requires an even number of forms")
|
||||
(let [e (in cls 1)]
|
||||
@[{:jolt/type :symbol :ns nil :name "if"}
|
||||
t e
|
||||
(build (tuple/slice cls 2))]))))))
|
||||
(build clauses))
|
||||
|
||||
(defn core-case
|
||||
"Macro: (case expr val1 result1 ... default)
|
||||
Supports single values, lists of values (one-of-many), and symbols."
|
||||
[expr & clauses]
|
||||
(def g (gensym))
|
||||
(defn make-const [c]
|
||||
(if (and (struct? c) (= :symbol (c :jolt/type)))
|
||||
@[{:jolt/type :symbol :ns nil :name "quote"} c]
|
||||
c))
|
||||
(defn make-test [c]
|
||||
(if (array? c)
|
||||
(let [or-args @[{:jolt/type :symbol :ns nil :name "or"}]]
|
||||
(each v c
|
||||
(array/push or-args @[{:jolt/type :symbol :ns nil :name "="} g (make-const v)]))
|
||||
or-args)
|
||||
@[{:jolt/type :symbol :ns nil :name "="} g (make-const c)]))
|
||||
(defn build [cls]
|
||||
(if (= 0 (length cls))
|
||||
nil
|
||||
(if (= 1 (length cls))
|
||||
(first cls)
|
||||
(let [c (first cls)
|
||||
r (first (tuple/slice cls 1))]
|
||||
@[{:jolt/type :symbol :ns nil :name "if"}
|
||||
(make-test c)
|
||||
r
|
||||
(build (tuple/slice cls 2))]))))
|
||||
@[{:jolt/type :symbol :ns nil :name "let*"} @[g expr] (build clauses)])
|
||||
|
||||
(defn core-when
|
||||
"Macro: (when test & body) -> (if test (do body...))"
|
||||
[test & body]
|
||||
|
|
@ -872,6 +1109,243 @@
|
|||
(array/push result sym)
|
||||
result)
|
||||
|
||||
(defn core-if-not
|
||||
"Macro: (if-not test then else?) -> (if (not test) then else?)"
|
||||
[test then-form & else-forms]
|
||||
@[{:jolt/type :symbol :ns nil :name "if"}
|
||||
@[{:jolt/type :symbol :ns nil :name "not"} test]
|
||||
then-form
|
||||
;else-forms])
|
||||
|
||||
(defn core-when-first
|
||||
"Macro: (when-first [sym coll] & body) -> (when-let [sym (first coll)] body...)"
|
||||
[bindings & body]
|
||||
(def sym (in bindings 0))
|
||||
(def coll-form (in bindings 1))
|
||||
@[{:jolt/type :symbol :ns nil :name "when-let"}
|
||||
@[sym @[{:jolt/type :symbol :ns nil :name "first"} coll-form]]
|
||||
;body])
|
||||
|
||||
(defn core-condp
|
||||
"Macro: (condp pred expr clause1 val1 ... default)"
|
||||
[pred expr & clauses]
|
||||
(def g (gensym))
|
||||
(defn build [cls]
|
||||
(if (= 0 (length cls))
|
||||
nil
|
||||
(if (= 1 (length cls))
|
||||
(first cls)
|
||||
(let [c (first cls)
|
||||
v (first (tuple/slice cls 1))]
|
||||
@[{:jolt/type :symbol :ns nil :name "if"}
|
||||
(if (and (struct? c) (= :symbol (c :jolt/type)) (= ":>>" (c :name)))
|
||||
@[v g]
|
||||
@[pred c g])
|
||||
v
|
||||
(build (tuple/slice cls 2))]))))
|
||||
@[{:jolt/type :symbol :ns nil :name "let*"} @[g expr] (build clauses)])
|
||||
|
||||
(defn core-dotimes
|
||||
"Macro: (dotimes [sym n] & body) -> loop from 0 to n-1"
|
||||
[bindings & body]
|
||||
(def sym (in bindings 0))
|
||||
(def n-form (in bindings 1))
|
||||
(def i (gensym))
|
||||
@[{:jolt/type :symbol :ns nil :name "let*"}
|
||||
@[i n-form]
|
||||
@[{:jolt/type :symbol :ns nil :name "loop*"}
|
||||
@[sym 0]
|
||||
@[{:jolt/type :symbol :ns nil :name "if"}
|
||||
@[{:jolt/type :symbol :ns nil :name "<"} sym i]
|
||||
@[{:jolt/type :symbol :ns nil :name "do"}
|
||||
;body
|
||||
@[{:jolt/type :symbol :ns nil :name "recur"}
|
||||
@[{:jolt/type :symbol :ns nil :name "inc"} sym]]]
|
||||
nil]]])
|
||||
|
||||
(defn core-while
|
||||
"Macro: (while test & body) -> loop while test is truthy"
|
||||
[test & body]
|
||||
@[{:jolt/type :symbol :ns nil :name "loop*"}
|
||||
@[]
|
||||
@[{:jolt/type :symbol :ns nil :name "when"}
|
||||
test
|
||||
@[{:jolt/type :symbol :ns nil :name "do"} ;body]
|
||||
@[{:jolt/type :symbol :ns nil :name "recur"}]]])
|
||||
|
||||
(defn core-for
|
||||
"Macro: (for [binding-form coll :when test :let [bindings]] body)
|
||||
List comprehension. Basic support for :when and :let."
|
||||
[bindings body]
|
||||
(defn parse-groups [bvec]
|
||||
(var groups @[])
|
||||
(var i 0)
|
||||
(while (< i (length bvec))
|
||||
(def bind (bvec i))
|
||||
(def coll (bvec (+ i 1)))
|
||||
(def mods @[])
|
||||
(+= i 2)
|
||||
(while (and (< i (length bvec)) (keyword? (bvec i)))
|
||||
(case (bvec i)
|
||||
:when (do (array/push mods @[{:jolt/type :symbol :ns nil :name "when"} (bvec (+ i 1))]) (+= i 2))
|
||||
:let (do (array/push mods @[{:jolt/type :symbol :ns nil :name "let"} (bvec (+ i 1))]) (+= i 2))
|
||||
:while (do (+= i 2))
|
||||
(do (+= i 1))))
|
||||
(array/push groups @[bind coll mods]))
|
||||
groups)
|
||||
(defn wrap-mods [mods inner-form]
|
||||
(if (= 0 (length mods))
|
||||
inner-form
|
||||
(let [m (in mods (- (length mods) 1))
|
||||
rest-mods (array/slice mods 0 (- (length mods) 1))
|
||||
kind (get (m 0) :name)]
|
||||
(wrap-mods rest-mods
|
||||
(if (= kind "when")
|
||||
@[{:jolt/type :symbol :ns nil :name "if"} (m 1)
|
||||
@[{:jolt/type :symbol :ns nil :name "list"} inner-form] @[]]
|
||||
@[{:jolt/type :symbol :ns nil :name "let*"} (m 1) inner-form])))))
|
||||
(defn build [group-idx groups]
|
||||
(if (>= group-idx (length groups))
|
||||
body
|
||||
(let [g (in groups group-idx)
|
||||
my-bind (in g 0)
|
||||
my-coll (in g 1)
|
||||
my-mods (in g 2)
|
||||
inner (build (+ group-idx 1) groups)
|
||||
inner-form (wrap-mods my-mods inner)
|
||||
is-last (= group-idx (- (length groups) 1))
|
||||
has-mods (> (length my-mods) 0)]
|
||||
(if (and is-last (not has-mods))
|
||||
@[{:jolt/type :symbol :ns nil :name "map"}
|
||||
@[{:jolt/type :symbol :ns nil :name "fn"} [my-bind] inner-form]
|
||||
my-coll]
|
||||
@[{:jolt/type :symbol :ns nil :name "mapcat"}
|
||||
@[{:jolt/type :symbol :ns nil :name "fn"} [my-bind] inner-form]
|
||||
my-coll]))))
|
||||
(if (>= (length bindings) 2)
|
||||
(build 0 (parse-groups bindings))
|
||||
body))
|
||||
|
||||
(defn core-thread-first
|
||||
"Macro: (-> x & forms) — thread first"
|
||||
[x & forms]
|
||||
(if (= 0 (length forms)) x
|
||||
(let [f (first forms)
|
||||
rest-forms (tuple/slice forms 1)]
|
||||
(if (array? f)
|
||||
(apply core-thread-first [(let [arr (array/slice f)]
|
||||
(array/insert arr 1 x)
|
||||
arr) ;rest-forms])
|
||||
(apply core-thread-first [@[f x] ;rest-forms])))))
|
||||
|
||||
(defn core-thread-last
|
||||
"Macro: (->> x & forms) — thread last"
|
||||
[x & forms]
|
||||
(if (= 0 (length forms)) x
|
||||
(let [f (first forms)
|
||||
rest-forms (tuple/slice forms 1)]
|
||||
(if (array? f)
|
||||
(apply core-thread-last [(let [arr (array/slice f)]
|
||||
(array/push arr x)
|
||||
arr) ;rest-forms])
|
||||
(apply core-thread-last [@[f x] ;rest-forms])))))
|
||||
|
||||
(defn core-some->
|
||||
"Macro: (some-> expr & forms) — thread first, stop at nil"
|
||||
[expr & forms]
|
||||
(if (= 0 (length forms)) expr
|
||||
(let [f (first forms)
|
||||
rest-forms (tuple/slice forms 1)]
|
||||
@[{:jolt/type :symbol :ns nil :name "let*"}
|
||||
@[{:jolt/type :symbol :ns nil :name "some->__x"} expr]
|
||||
@[{:jolt/type :symbol :ns nil :name "if"}
|
||||
@[{:jolt/type :symbol :ns nil :name "some?"}
|
||||
{:jolt/type :symbol :ns nil :name "some->__x"}]
|
||||
@[{:jolt/type :symbol :ns nil :name "let*"}
|
||||
@[{:jolt/type :symbol :ns nil :name "some->__x"}
|
||||
(if (array? f)
|
||||
(let [arr (array/slice f)]
|
||||
(array/insert arr 1 {:jolt/type :symbol :ns nil :name "some->__x"})
|
||||
arr)
|
||||
@[f {:jolt/type :symbol :ns nil :name "some->__x"}])]
|
||||
(apply core-some-> [{:jolt/type :symbol :ns nil :name "some->__x"} ;rest-forms])]
|
||||
nil]])))
|
||||
|
||||
(defn core-some->>
|
||||
"Macro: (some->> expr & forms) — thread last, stop at nil"
|
||||
[expr & forms]
|
||||
(if (= 0 (length forms)) expr
|
||||
(let [f (first forms)
|
||||
rest-forms (tuple/slice forms 1)]
|
||||
@[{:jolt/type :symbol :ns nil :name "let*"}
|
||||
@[{:jolt/type :symbol :ns nil :name "some->__x"} expr]
|
||||
@[{:jolt/type :symbol :ns nil :name "if"}
|
||||
@[{:jolt/type :symbol :ns nil :name "some?"}
|
||||
{:jolt/type :symbol :ns nil :name "some->__x"}]
|
||||
@[{:jolt/type :symbol :ns nil :name "let*"}
|
||||
@[{:jolt/type :symbol :ns nil :name "some->__x"}
|
||||
(if (array? f)
|
||||
(let [arr (array/slice f)]
|
||||
(array/push arr {:jolt/type :symbol :ns nil :name "some->__x"})
|
||||
arr)
|
||||
@[f {:jolt/type :symbol :ns nil :name "some->__x"}])]
|
||||
(apply core-some->> [{:jolt/type :symbol :ns nil :name "some->__x"} ;rest-forms])]
|
||||
nil]])))
|
||||
|
||||
(defn core-cond->
|
||||
"Macro: (cond-> expr test form ...) — thread first only when test is true"
|
||||
[expr & clauses]
|
||||
(def g (gensym))
|
||||
(defn build [cls result-form]
|
||||
(if (= 0 (length cls))
|
||||
result-form
|
||||
(let [t (first cls)
|
||||
f (in cls 1)
|
||||
f-call (if (array? f)
|
||||
(let [arr (array/slice f)]
|
||||
(array/insert arr 1 result-form)
|
||||
arr)
|
||||
@[f result-form])]
|
||||
(build (tuple/slice cls 2)
|
||||
@[{:jolt/type :symbol :ns nil :name "if"}
|
||||
t
|
||||
f-call
|
||||
result-form]))))
|
||||
@[{:jolt/type :symbol :ns nil :name "let*"} @[g expr] (build clauses g)])
|
||||
|
||||
(defn core-cond->>
|
||||
"Macro: (cond->> expr test form ...) — thread last only when test is true"
|
||||
[expr & clauses]
|
||||
(def g (gensym))
|
||||
(defn build [cls result-form]
|
||||
(if (= 0 (length cls))
|
||||
result-form
|
||||
(let [t (first cls)
|
||||
f (in cls 1)
|
||||
f-call (if (array? f)
|
||||
(let [arr (array/slice f)]
|
||||
(array/push arr result-form)
|
||||
arr)
|
||||
@[f result-form])]
|
||||
(build (tuple/slice cls 2)
|
||||
@[{:jolt/type :symbol :ns nil :name "if"}
|
||||
t
|
||||
f-call
|
||||
result-form]))))
|
||||
@[{:jolt/type :symbol :ns nil :name "let*"} @[g expr] (build clauses g)])
|
||||
|
||||
(defn core-as->
|
||||
"Macro: (as-> expr name & forms) — bind name to expr, thread through forms"
|
||||
[expr name & forms]
|
||||
(defn build [fs acc]
|
||||
(if (= 0 (length fs))
|
||||
acc
|
||||
(let [f (first fs)]
|
||||
@[{:jolt/type :symbol :ns nil :name "let*"}
|
||||
@[name acc]
|
||||
(build (tuple/slice fs 1) f)])))
|
||||
(build forms expr))
|
||||
|
||||
(defn core-push-thread-bindings [b] (push-thread-bindings b))
|
||||
(defn core-pop-thread-bindings [] (pop-thread-bindings))
|
||||
|
||||
|
|
@ -1228,6 +1702,9 @@
|
|||
"neg?" core-neg?
|
||||
"even?" core-even?
|
||||
"odd?" core-odd?
|
||||
"integer?" core-integer?
|
||||
"boolean?" core-boolean?
|
||||
"list?" core-list?
|
||||
"empty?" core-empty?
|
||||
"every?" core-every?
|
||||
"+" core-+
|
||||
|
|
@ -1241,6 +1718,9 @@
|
|||
"quot" core-quot
|
||||
"max" core-max
|
||||
"min" core-min
|
||||
"abs" core-abs
|
||||
"rand" core-rand
|
||||
"rand-int" core-rand-int
|
||||
"=" core-=
|
||||
"not=" core-not=
|
||||
"<" core-<
|
||||
|
|
@ -1308,6 +1788,7 @@
|
|||
"disj" core-disj
|
||||
"lazy-seq" core-lazy-seq
|
||||
"lazy-cat" core-lazy-cat
|
||||
"coll->cells" coll->cells
|
||||
"make-lazy-seq" make-lazy-seq
|
||||
"str" core-str
|
||||
"name" core-name
|
||||
|
|
@ -1357,16 +1838,38 @@
|
|||
"deref" core-deref
|
||||
"reset!" core-reset!
|
||||
"swap!" core-swap!
|
||||
"swap-vals!" core-swap-vals!
|
||||
"reset-vals!" core-reset-vals!
|
||||
"compare-and-set!" core-compare-and-set!
|
||||
"set-validator!" core-set-validator!
|
||||
"get-validator" core-get-validator
|
||||
"add-watch" core-add-watch
|
||||
"remove-watch" core-remove-watch
|
||||
"not" core-not
|
||||
"and" core-and
|
||||
"or" core-or
|
||||
"cond" core-cond
|
||||
"case" core-case
|
||||
"for" core-for
|
||||
"when" core-when
|
||||
"when-not" core-when-not
|
||||
"if-not" core-if-not
|
||||
"when-first" core-when-first
|
||||
"if-let" core-if-let
|
||||
"when-let" core-when-let
|
||||
"if-some" core-if-some
|
||||
"when-some" core-when-some
|
||||
"doto" core-doto
|
||||
"condp" core-condp
|
||||
"dotimes" core-dotimes
|
||||
"while" core-while
|
||||
"->" core-thread-first
|
||||
"->>" core-thread-last
|
||||
"some->" core-some->
|
||||
"some->>" core-some->>
|
||||
"cond->" core-cond->
|
||||
"cond->>" core-cond->>
|
||||
"as->" core-as->
|
||||
"defn" core-defn
|
||||
"defn-" core-defn-
|
||||
"derive" core-derive
|
||||
|
|
@ -1434,7 +1937,7 @@
|
|||
(defn core-macro-names
|
||||
"Set of core binding names that are macros."
|
||||
[]
|
||||
@{"and" true "or" true "when" true "when-not" true "if-let" true "when-let" true "if-some" true "when-some" true "doto" true "defn" true "defn-" true "declare" true "fn" true "let" true "loop" true "defrecord" true "defprotocol" true "extend-type" true "extend-protocol" true "extend" true "reify" true "proxy" true "definterface" true "comment" true "binding" true "lazy-seq" true "lazy-cat" true})
|
||||
@{"and" true "or" true "cond" true "case" true "for" true "when" true "when-not" true "if-let" true "when-let" true "if-some" true "when-some" true "doto" true "defn" true "defn-" true "declare" true "fn" true "let" true "loop" true "defrecord" true "defprotocol" true "extend-type" true "extend-protocol" true "extend" true "reify" true "proxy" true "definterface" true "comment" true "binding" true "lazy-seq" true "lazy-cat" true "if-not" true "when-first" true "condp" true "dotimes" true "while" true "some->" true "some->>" true "cond->" true "cond->>" true "as->" true "->" true "->>" true})
|
||||
|
||||
(def init-core!
|
||||
(fn [& args]
|
||||
|
|
|
|||
|
|
@ -261,14 +261,16 @@
|
|||
(if (> (length form) 3) (eval-form ctx bindings (in form 3)) nil)))
|
||||
"def" (let [raw-name (in form 1)
|
||||
name-sym (unwrap-meta-name raw-name)
|
||||
val (eval-form ctx bindings (in form 2))
|
||||
# Check for ^:dynamic metadata
|
||||
dynamic? (and (array? raw-name) (> (length raw-name) 0)
|
||||
(sym-name? (first raw-name) "with-meta")
|
||||
(= :dynamic (last raw-name)))
|
||||
ns-name (ctx-current-ns ctx)
|
||||
ns (ctx-find-ns ctx ns-name)]
|
||||
(def v (ns-intern ns (name-sym :name) val))
|
||||
ns (ctx-find-ns ctx ns-name)
|
||||
# Create var first (unbound) so self-referencing defs resolve
|
||||
v (ns-intern ns (name-sym :name))
|
||||
val (eval-form ctx bindings (in form 2))]
|
||||
(bind-root v val)
|
||||
(when dynamic?
|
||||
(put v :dynamic true))
|
||||
(var-get v))
|
||||
|
|
|
|||
|
|
@ -114,45 +114,61 @@
|
|||
m)
|
||||
|
||||
# ============================================================
|
||||
# LazySeq — realize-once lazy sequence
|
||||
# LazySeq — cell-by-cell lazy sequence (Clojure-compatible)
|
||||
# ============================================================
|
||||
# Model: thunk returns nil (empty) or [first-val, rest-thunk] pair.
|
||||
# Each step produces one element + thunk for the rest.
|
||||
# Supports self-referencing sequences like fib-seq.
|
||||
|
||||
(defn lazy-seq?
|
||||
"Check if x is a LazySeq."
|
||||
[x]
|
||||
(and (table? x) (= :jolt/lazy-seq (x :jolt/type))))
|
||||
|
||||
(defn make-lazy-seq [thunk]
|
||||
@{:jolt/type :jolt/lazy-seq :fn thunk :realized false :val nil})
|
||||
|
||||
(defn realize-ls
|
||||
"Force a LazySeq. Returns the realized value, caching it."
|
||||
"Force a LazySeq cell. Returns nil (empty) or [first-val, rest-thunk]."
|
||||
[ls]
|
||||
(if (get ls :realized)
|
||||
(ls :val)
|
||||
(let [v ((ls :fn))
|
||||
vf (if (lazy-seq? v) (realize-ls v) v)]
|
||||
(let [v ((ls :fn))]
|
||||
(put ls :realized true)
|
||||
(put ls :val vf)
|
||||
vf)))
|
||||
(put ls :val v)
|
||||
v)))
|
||||
|
||||
(defn ls-first [ls]
|
||||
(let [val (realize-ls ls)]
|
||||
(if (nil? val) nil
|
||||
(if (and (indexed? val) (> (length val) 0)) (in val 0) nil))))
|
||||
(let [cell (realize-ls ls)]
|
||||
(if (nil? cell) nil (in cell 0))))
|
||||
|
||||
(defn ls-rest [ls]
|
||||
(let [val (realize-ls ls)]
|
||||
(if (nil? val) nil
|
||||
(if (indexed? val) (if (tuple? val) (tuple/slice val 1) (array/slice val 1)) nil))))
|
||||
(let [cell (realize-ls ls)]
|
||||
(if (nil? cell) nil
|
||||
(let [rt (in cell 1)]
|
||||
(if (nil? rt) nil (make-lazy-seq rt))))))
|
||||
|
||||
(defn ls-seq [ls]
|
||||
(let [val (realize-ls ls)]
|
||||
(when val (if (tuple? val) (tuple/slice val) (if (array? val) (array/slice val) val)))))
|
||||
(var result @[])
|
||||
(var cur ls)
|
||||
(while (not (nil? cur))
|
||||
(let [cell (realize-ls cur)]
|
||||
(if (nil? cell) (break))
|
||||
(array/push result (in cell 0))
|
||||
(let [rt (in cell 1)]
|
||||
(set cur (if (nil? rt) nil (make-lazy-seq rt))))))
|
||||
(if (= 0 (length result)) nil result))
|
||||
|
||||
(defn ls-count [ls]
|
||||
(let [val (realize-ls ls)]
|
||||
(if (nil? val) 0 (length val))))
|
||||
|
||||
(defn make-lazy-seq [thunk]
|
||||
@{:jolt/type :jolt/lazy-seq :fn thunk :realized false :val nil})
|
||||
(var cnt 0)
|
||||
(var cur ls)
|
||||
(while (not (nil? cur))
|
||||
(let [cell (realize-ls cur)]
|
||||
(if (nil? cell) (break))
|
||||
(++ cnt)
|
||||
(let [rt (in cell 1)]
|
||||
(set cur (if (nil? rt) nil (make-lazy-seq rt))))))
|
||||
cnt)
|
||||
|
||||
# ============================================================
|
||||
# PersistentHashSet — backed by PersistentHashMap
|
||||
|
|
|
|||
154
test/clojure-atom-test.janet
Normal file
154
test/clojure-atom-test.janet
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
# Ported from clojure/test_clojure/atoms.clj + systematic atom tests
|
||||
(use ../src/jolt/api)
|
||||
(defn ct-eval [ctx s] (eval-string ctx s))
|
||||
|
||||
(print "Ported Atom Tests")
|
||||
|
||||
# --- atom creation ---
|
||||
(print "test atom creation...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx "(atom? (atom 0))")) "atom?")
|
||||
(assert (= false (ct-eval ctx "(atom? nil)")) "atom? nil")
|
||||
(assert (= false (ct-eval ctx "(atom? 42)")) "atom? number")
|
||||
(assert (= false (ct-eval ctx "(atom? \"x\")")) "atom? string")
|
||||
(assert (= 42 (ct-eval ctx "(deref (atom 42))")) "deref atom")
|
||||
(assert (= 99 (ct-eval ctx "(let [a (atom 99)] @a)")) "@ deref macro"))
|
||||
(print " ok")
|
||||
|
||||
# --- deref on non-atoms ---
|
||||
(print "test deref...")
|
||||
(let [ctx (init)]
|
||||
(assert (= 1 (ct-eval ctx "(deref 1)")) "deref non-atom passes through")
|
||||
(assert (= "x" (ct-eval ctx "(deref \"x\")")) "deref string passes through")
|
||||
(assert (= nil (ct-eval ctx "(deref nil)")) "deref nil"))
|
||||
(print " ok")
|
||||
|
||||
# --- reset! ---
|
||||
(print "test reset!...")
|
||||
(let [ctx (init)]
|
||||
(assert (= :b (ct-eval ctx "(let [a (atom :a)] (reset! a :b))")) "reset! returns new")
|
||||
(assert (= 42 (ct-eval ctx "(let [a (atom 0)] (reset! a 42) @a)")) "reset! updates value")
|
||||
(assert (= true (ct-eval ctx "(= [1 1] (let [a (atom 0)] [(reset! a 1) @a]))")) "reset! returns new, value changed"))
|
||||
(print " ok")
|
||||
|
||||
# --- swap! ---
|
||||
(print "test swap!...")
|
||||
(let [ctx (init)]
|
||||
(assert (= 1 (ct-eval ctx "(let [a (atom 0)] (swap! a inc))")) "swap! inc returns new")
|
||||
(assert (= 2 (ct-eval ctx "(let [a (atom 0)] (swap! a + 2))")) "swap! + 2")
|
||||
(assert (= 3 (ct-eval ctx "(let [a (atom 0)] (swap! a + 1 2) @a)")) "swap! + 1 2")
|
||||
(assert (= 6 (ct-eval ctx "(let [a (atom 0)] (swap! a + 1 2 3) @a)")) "swap! + 1 2 3")
|
||||
(assert (= 10 (ct-eval ctx "(let [a (atom 0)] (swap! a + 1 2 3 4) @a)")) "swap! + 1 2 3 4"))
|
||||
(print " ok")
|
||||
|
||||
# --- swap-vals! (returns [old new]) ---
|
||||
(print "test swap-vals!...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx
|
||||
"(= [0 1] (let [a (atom 0)] (swap-vals! a inc)))")) "swap-vals! inc")
|
||||
(assert (= true (ct-eval ctx
|
||||
"(= [1 2] (let [a (atom 1)] (swap-vals! a inc)))")) "swap-vals! inc from 1")
|
||||
(assert (= 2 (ct-eval ctx
|
||||
"(let [a (atom 1)] (swap-vals! a inc) @a)")) "swap-vals! updates value"))
|
||||
(print " ok")
|
||||
|
||||
# --- swap-vals! with extra args ---
|
||||
(print "test swap-vals! arities...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx
|
||||
"(= [0 1] (let [a (atom 0)] (swap-vals! a + 1)))")) "swap-vals! + 1")
|
||||
(assert (= true (ct-eval ctx
|
||||
"(= [1 3] (let [a (atom 0)] (swap-vals! a + 1) (swap-vals! a + 1 1)))")) "swap-vals! + 1 1")
|
||||
(assert (= true (ct-eval ctx
|
||||
"(= [3 6] (let [a (atom 0)] (swap-vals! a + 1) (swap-vals! a + 1 1) (swap-vals! a + 1 1 1)))")) "swap-vals! + 1 1 1")
|
||||
(assert (= true (ct-eval ctx
|
||||
"(= [6 10] (let [a (atom 0)] (swap-vals! a + 1) (swap-vals! a + 1 1) (swap-vals! a + 1 1 1) (swap-vals! a + 1 1 1 1)))")) "swap-vals! + 1 1 1 1"))
|
||||
(print " ok")
|
||||
|
||||
# --- reset-vals! (returns [old new]) ---
|
||||
(print "test reset-vals!...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx
|
||||
"(= [0 :b] (let [a (atom 0)] (reset-vals! a :b)))")) "reset-vals! returns old new")
|
||||
(assert (= true (ct-eval ctx
|
||||
"(= [:b 42] (let [a (atom 0)] (reset-vals! a :b) (reset-vals! a 42)))")) "reset-vals! chain")
|
||||
(assert (= 42 (ct-eval ctx
|
||||
"(let [a (atom 0)] (reset-vals! a :b) (reset-vals! a 42) @a)")) "reset-vals! updates value"))
|
||||
(print " ok")
|
||||
|
||||
# --- compare-and-set! ---
|
||||
(print "test compare-and-set!...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx
|
||||
"(let [a (atom 0)] (compare-and-set! a 0 1))")) "CAS true match")
|
||||
(assert (= true (ct-eval ctx
|
||||
"(= [true 1] (let [a (atom 0)] [(compare-and-set! a 0 1) @a]))")) "CAS true + value changed")
|
||||
(assert (= true (ct-eval ctx
|
||||
"(= [false 0] (let [a (atom 0)] [(compare-and-set! a 1 2) @a]))")) "CAS false no match")
|
||||
(assert (= true (ct-eval ctx
|
||||
"(= [false 1] (let [a (atom 0)] (compare-and-set! a 0 1) [(compare-and-set! a 0 2) @a]))")) "CAS false after change"))
|
||||
(print " ok")
|
||||
|
||||
# --- validator ---
|
||||
(print "test validator...")
|
||||
(let [ctx (init)]
|
||||
(assert (= 42 (ct-eval ctx
|
||||
"(let [a (atom 0 :validator pos?)] (reset! a 42) @a)")) "validator passes")
|
||||
(assert (= true (ct-eval ctx
|
||||
"(= false (try (let [a (atom 0 :validator pos?)] (reset! a -1) true) (catch Exception e false)))")) "validator blocks invalid reset!")
|
||||
(assert (= true (ct-eval ctx
|
||||
"(= false (try (let [a (atom 0 :validator pos?)] (swap! a (fn [x] -1)) true) (catch Exception e false)))")) "validator blocks invalid swap!")
|
||||
(assert (= nil (ct-eval ctx "(set-validator! (atom 0) pos?)")) "set-validator! returns nil")
|
||||
(assert (= nil (ct-eval ctx "(get-validator (atom 0))")) "get-validator nil default")
|
||||
(assert (= true (ct-eval ctx
|
||||
"(= even? (do (def a (atom 0)) (set-validator! a even?) (get-validator a)))")) "get-validator returns set fn"))
|
||||
(print " ok")
|
||||
|
||||
# --- watches ---
|
||||
(print "test watches...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx
|
||||
"(empty? (:watches (atom 0)))")) "atom starts with empty watches")
|
||||
(assert (= true (ct-eval ctx
|
||||
"(= [0 42] (let [a (atom 0)
|
||||
w (atom nil)]
|
||||
(add-watch a :my-key (fn [k ref old new] (reset! w [old new])))
|
||||
(swap! a + 42)
|
||||
@w))")) "add-watch triggers on swap!")
|
||||
(assert (= true (ct-eval ctx
|
||||
"(= :unchanged (let [a (atom 0)
|
||||
w (atom :unchanged)]
|
||||
(add-watch a :x (fn [_ _ _ _] (reset! w :fired)))
|
||||
(remove-watch a :x)
|
||||
(swap! a inc)
|
||||
@w))")) "remove-watch stops notification")
|
||||
(assert (= true (ct-eval ctx
|
||||
"(= 2 (let [a (atom 0)]
|
||||
(add-watch a :foo (fn [_ _ _ _] nil))
|
||||
(add-watch a :bar (fn [_ _ _ _] nil))
|
||||
(count (:watches a))))")) "multiple watches")
|
||||
(assert (= true (ct-eval ctx
|
||||
"(= 0 (let [a (atom 0)]
|
||||
(add-watch a :foo (fn [_ _ _ _] nil))
|
||||
(remove-watch a :foo)
|
||||
(count (:watches a))))")) "remove-watch clears count")
|
||||
(assert (= true (ct-eval ctx
|
||||
"(= 2 (let [a (atom 0)
|
||||
fired (atom [])]
|
||||
(add-watch a :w1 (fn [_ _ o n] (swap! fired conj [:w1 o n])))
|
||||
(add-watch a :w2 (fn [_ _ o n] (swap! fired conj [:w2 o n])))
|
||||
(reset! a 99)
|
||||
(count @fired)))")) "multiple watches both fire"))
|
||||
(print " ok")
|
||||
|
||||
# --- metadata on atoms ---
|
||||
(print "test atom metadata...")
|
||||
(let [ctx (init)]
|
||||
(assert (= nil (ct-eval ctx "(meta (atom 0))")) "atom meta nil by default")
|
||||
(assert (= true (ct-eval ctx
|
||||
"(= {:foo \"bar\"} (meta (atom 0 :meta {:foo \"bar\"})))")) "atom with :meta")
|
||||
(assert (= true (ct-eval ctx
|
||||
"(= {:validated true} (meta (atom 0 :validator pos? :meta {:validated true})))")) "atom with validator and meta"))
|
||||
(print " ok")
|
||||
|
||||
(print "\nAll Ported Atom tests passed!")
|
||||
151
test/clojure-control-test.janet
Normal file
151
test/clojure-control-test.janet
Normal 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!")
|
||||
35
test/clojure-for-test.janet
Normal file
35
test/clojure-for-test.janet
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# Ported from clojure/test_clojure/for.clj
|
||||
(use ../src/jolt/api)
|
||||
(defn ct-eval [ctx s] (eval-string ctx s))
|
||||
|
||||
(print "Ported For Tests")
|
||||
|
||||
# --- When ---
|
||||
(print "test-for :when...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx
|
||||
"(= (for [x (range 10) :when (odd? x)] x)
|
||||
(quote (1 3 5 7 9)))")) "for :when odd?")
|
||||
(assert (= true (ct-eval ctx
|
||||
"(= (for [x (range 4) y (range 4) :when (odd? y)] [x y])
|
||||
(quote ([0 1] [0 3] [1 1] [1 3] [2 1] [2 3] [3 1] [3 3])))")) "for nested :when"))
|
||||
(print " ok")
|
||||
|
||||
# --- Let ---
|
||||
(print "test-for :let...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx
|
||||
"(= (for [x (range 3) y (range 3) :let [z (+ x y)] :when (odd? z)] [x y z])
|
||||
(quote ([0 1 1] [1 0 1] [1 2 3] [2 1 3])))")) "for :let :when"))
|
||||
(print " ok")
|
||||
|
||||
# --- Nesting ---
|
||||
(print "test-for nesting...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx
|
||||
"(= (for [x (quote (a b)) y (interpose x (quote (1 2))) z (list x y)] [x y z])
|
||||
(quote ([a 1 a] [a 1 1] [a a a] [a a a] [a 2 a] [a 2 2]
|
||||
[b 1 b] [b 1 1] [b b b] [b b b] [b 2 b] [b 2 2])))")) "for nested interpose"))
|
||||
(print " ok")
|
||||
|
||||
(print "\nAll Ported For tests passed!")
|
||||
127
test/clojure-logic-test.janet
Normal file
127
test/clojure-logic-test.janet
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
# Ported from clojure/test_clojure/logic.clj
|
||||
(use ../src/jolt/api)
|
||||
(defn ct-eval [ctx s] (eval-string ctx s))
|
||||
|
||||
(print "Ported Logic Tests (from clojure/test-clojure/logic.clj)")
|
||||
|
||||
# --- test-if ---
|
||||
(print "test-if: true/false/nil...")
|
||||
(let [ctx (init)]
|
||||
(assert (= :t (ct-eval ctx "(if true :t)")) "if true")
|
||||
(assert (= :t (ct-eval ctx "(if true :t :f)")) "if true with else")
|
||||
(assert (= nil (ct-eval ctx "(if false :t)")) "if false no else")
|
||||
(assert (= :f (ct-eval ctx "(if false :t :f)")) "if false with else")
|
||||
(assert (= nil (ct-eval ctx "(if nil :t)")) "if nil no else")
|
||||
(assert (= :f (ct-eval ctx "(if nil :t :f)")) "if nil with else"))
|
||||
(print " ok")
|
||||
|
||||
(print "test-if: zero/empty is true...")
|
||||
(let [ctx (init)]
|
||||
(assert (= :t (ct-eval ctx "(if 0 :t :f)")) "0 is true")
|
||||
(assert (= :t (ct-eval ctx "(if 0.0 :t :f)")) "0.0 is true")
|
||||
(assert (= :t (ct-eval ctx "(if \"\" :t :f)")) "empty string is true")
|
||||
(assert (= :t (ct-eval ctx "(if () :t :f)")) "empty list is true")
|
||||
(assert (= :t (ct-eval ctx "(if [] :t :f)")) "empty vector is true")
|
||||
(assert (= :t (ct-eval ctx "(if {} :t :f)")) "empty map is true")
|
||||
(assert (= :t (ct-eval ctx "(if #{} :t :f)")) "empty set is true"))
|
||||
(print " ok")
|
||||
|
||||
(print "test-if: anything except nil/false is true...")
|
||||
(let [ctx (init)]
|
||||
(assert (= :t (ct-eval ctx "(if 42 :t :f)")) "42 is true")
|
||||
(assert (= :t (ct-eval ctx "(if 1.2 :t :f)")) "1.2 is true")
|
||||
(assert (= :t (ct-eval ctx "(if \"abc\" :t :f)")) "string is true")
|
||||
(assert (= :t (ct-eval ctx "(if 'abc :t :f)")) "symbol is true")
|
||||
(assert (= :t (ct-eval ctx "(if :kw :t :f)")) "keyword is true")
|
||||
(assert (= :t (ct-eval ctx "(if '(1 2) :t :f)")) "list is true")
|
||||
(assert (= :t (ct-eval ctx "(if [1 2] :t :f)")) "vector is true")
|
||||
(assert (= :t (ct-eval ctx "(if {:a 1 :b 2} :t :f)")) "map is true")
|
||||
(assert (= :t (ct-eval ctx "(if #{1 2} :t :f)")) "set is true"))
|
||||
(print " ok")
|
||||
|
||||
# --- test-nil-punning ---
|
||||
(print "test-nil-punning...")
|
||||
(let [ctx (init)]
|
||||
(assert (= :yes (ct-eval ctx "(if (first []) :no :yes)")) "first [] nil")
|
||||
(assert (= :yes (ct-eval ctx "(if (next [1]) :no :yes)")) "next [1] nil")
|
||||
(assert (= :no (ct-eval ctx "(if (rest [1]) :no :yes)")) "rest [1] non-nil")
|
||||
(assert (= :yes (ct-eval ctx "(if (seq nil) :no :yes)")) "seq nil")
|
||||
(assert (= :yes (ct-eval ctx "(if (seq []) :no :yes)")) "seq [] nil")
|
||||
(assert (= :no (ct-eval ctx "(if (lazy-seq nil) :no :yes)")) "lazy-seq nil non-nil")
|
||||
(assert (= :no (ct-eval ctx "(if (lazy-seq []) :no :yes)")) "lazy-seq [] non-nil")
|
||||
(assert (= :no (ct-eval ctx "(if (filter (fn [x] (> x 10)) [1 2 3]) :no :yes)")) "filter non-match non-nil")
|
||||
(assert (= :no (ct-eval ctx "(if (map identity []) :no :yes)")) "map empty non-nil")
|
||||
(assert (= :no (ct-eval ctx "(if (apply concat []) :no :yes)")) "apply concat [] non-nil")
|
||||
(assert (= :no (ct-eval ctx "(if (concat) :no :yes)")) "concat empty non-nil")
|
||||
(assert (= :no (ct-eval ctx "(if (concat []) :no :yes)")) "concat [] non-nil")
|
||||
(assert (= :no (ct-eval ctx "(if (reverse nil) :no :yes)")) "reverse nil non-nil")
|
||||
(assert (= :no (ct-eval ctx "(if (reverse []) :no :yes)")) "reverse [] non-nil")
|
||||
(assert (= :no (ct-eval ctx "(if (sort nil) :no :yes)")) "sort nil non-nil")
|
||||
(assert (= :no (ct-eval ctx "(if (sort []) :no :yes)")) "sort [] non-nil"))
|
||||
(print " ok")
|
||||
|
||||
# --- test-and ---
|
||||
(print "test-and...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx "(and)")) "and empty")
|
||||
(assert (= true (ct-eval ctx "(and true)")) "and true")
|
||||
(assert (= nil (ct-eval ctx "(and nil)")) "and nil")
|
||||
(assert (= false (ct-eval ctx "(and false)")) "and false")
|
||||
(assert (= nil (ct-eval ctx "(and true nil)")) "and true nil")
|
||||
(assert (= false (ct-eval ctx "(and true false)")) "and true false")
|
||||
(assert (= "abc" (ct-eval ctx "(and 1 true :kw 'abc \"abc\")")) "and chain last")
|
||||
(assert (= nil (ct-eval ctx "(and 1 true :kw nil 'abc \"abc\")")) "and chain nil")
|
||||
(assert (= false (ct-eval ctx "(and 1 true :kw 'abc \"abc\" false)")) "and chain false"))
|
||||
(print " ok")
|
||||
|
||||
# --- test-or ---
|
||||
(print "test-or...")
|
||||
(let [ctx (init)]
|
||||
(assert (= nil (ct-eval ctx "(or)")) "or empty")
|
||||
(assert (= true (ct-eval ctx "(or true)")) "or true")
|
||||
(assert (= nil (ct-eval ctx "(or nil)")) "or nil")
|
||||
(assert (= false (ct-eval ctx "(or false)")) "or false")
|
||||
(assert (= true (ct-eval ctx "(or nil false true)")) "or nil false true")
|
||||
(assert (= 1 (ct-eval ctx "(or nil false 1 2)")) "or picks first truthy")
|
||||
(assert (= "abc" (ct-eval ctx "(or nil false \"abc\" :kw)")) "or picks string")
|
||||
(assert (= nil (ct-eval ctx "(or false nil)")) "or false nil -> nil")
|
||||
(assert (= false (ct-eval ctx "(or nil false)")) "or nil false -> false")
|
||||
(assert (= false (ct-eval ctx "(or nil nil nil false)")) "or chain to false")
|
||||
(assert (= true (ct-eval ctx "(or nil true false)")) "or nil true false"))
|
||||
(print " ok")
|
||||
|
||||
# --- test-not ---
|
||||
(print "test-not...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx "(not nil)")) "not nil")
|
||||
(assert (= true (ct-eval ctx "(not false)")) "not false")
|
||||
(assert (= false (ct-eval ctx "(not true)")) "not true")
|
||||
(assert (= false (ct-eval ctx "(not 0)")) "not 0")
|
||||
(assert (= false (ct-eval ctx "(not 0.0)")) "not 0.0")
|
||||
(assert (= false (ct-eval ctx "(not 42)")) "not 42")
|
||||
(assert (= false (ct-eval ctx "(not 1.2)")) "not 1.2")
|
||||
(assert (= false (ct-eval ctx "(not \"\")")) "not empty string")
|
||||
(assert (= false (ct-eval ctx "(not \"abc\")")) "not string")
|
||||
(assert (= false (ct-eval ctx "(not 'abc)")) "not symbol")
|
||||
(assert (= false (ct-eval ctx "(not :kw)")) "not keyword")
|
||||
(assert (= false (ct-eval ctx "(not ())")) "not empty list")
|
||||
(assert (= false (ct-eval ctx "(not '(1 2))")) "not list")
|
||||
(assert (= false (ct-eval ctx "(not []))")) "not empty vector")
|
||||
(assert (= false (ct-eval ctx "(not [1 2])")) "not vector")
|
||||
(assert (= false (ct-eval ctx "(not {})")) "not empty map")
|
||||
(assert (= false (ct-eval ctx "(not {:a 1 :b 2})")) "not map")
|
||||
(assert (= false (ct-eval ctx "(not #{})")) "not empty set")
|
||||
(assert (= false (ct-eval ctx "(not #{1 2})")) "not set"))
|
||||
(print " ok")
|
||||
|
||||
# --- test-some? ---
|
||||
(print "test-some?...")
|
||||
(let [ctx (init)]
|
||||
(assert (= false (ct-eval ctx "(some? nil)")) "some? nil")
|
||||
(assert (= true (ct-eval ctx "(some? false)")) "some? false")
|
||||
(assert (= true (ct-eval ctx "(some? 0)")) "some? 0")
|
||||
(assert (= true (ct-eval ctx "(some? \"abc\")")) "some? string")
|
||||
(assert (= true (ct-eval ctx "(some? [])")) "some? empty vec"))
|
||||
(print " ok")
|
||||
|
||||
(print "\nAll Ported Logic tests passed!")
|
||||
63
test/clojure-macros-test.janet
Normal file
63
test/clojure-macros-test.janet
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
# Ported from clojure/test_clojure/macros.clj
|
||||
(use ../src/jolt/api)
|
||||
(defn ct-eval [ctx s] (eval-string ctx s))
|
||||
|
||||
(print "Ported Macros Tests")
|
||||
|
||||
# --- -> and ->> threading ---
|
||||
(print "test -> and ->>...")
|
||||
(let [ctx (init)]
|
||||
(ct-eval ctx "(defmacro c [arg] (if (= 'b (first arg)) :foo :bar))")
|
||||
(ct-eval ctx "(def a 2)")
|
||||
(ct-eval ctx "(def b identity)")
|
||||
(assert (= :foo (ct-eval ctx "(-> a b c)")) "-> threading")
|
||||
(assert (= :foo (ct-eval ctx "(->> a b c)")) "->> threading"))
|
||||
(print " ok")
|
||||
|
||||
# --- some-> ---
|
||||
(print "test some->...")
|
||||
(let [ctx (init)]
|
||||
(assert (= nil (ct-eval ctx "(some-> nil)")) "some-> nil")
|
||||
(assert (= 0 (ct-eval ctx "(some-> 0)")) "some-> 0")
|
||||
(assert (= -1 (ct-eval ctx "(some-> 1 (- 2))")) "some-> with form")
|
||||
(ct-eval ctx "(defn const-nil [_] nil)")
|
||||
(assert (= nil (ct-eval ctx "(some-> 1 const-nil (- 2))")) "some-> stop at nil"))
|
||||
(print " ok")
|
||||
|
||||
# --- some->> ---
|
||||
(print "test some->>...")
|
||||
(let [ctx (init)]
|
||||
(assert (= nil (ct-eval ctx "(some->> nil)")) "some->> nil")
|
||||
(assert (= 0 (ct-eval ctx "(some->> 0)")) "some->> 0")
|
||||
(assert (= 1 (ct-eval ctx "(some->> 1 (- 2))")) "some->> with form")
|
||||
(ct-eval ctx "(defn const-nil2 [_] nil)")
|
||||
(assert (= nil (ct-eval ctx "(some->> 1 const-nil2 (- 2))")) "some->> stop at nil"))
|
||||
(print " ok")
|
||||
|
||||
# --- cond-> ---
|
||||
(print "test cond->...")
|
||||
(let [ctx (init)]
|
||||
(assert (= 0 (ct-eval ctx "(cond-> 0)")) "cond-> single")
|
||||
(assert (= -1 (ct-eval ctx "(cond-> 0 true inc true (- 2))")) "cond-> with tests")
|
||||
(assert (= 0 (ct-eval ctx "(cond-> 0 false inc)")) "cond-> false test")
|
||||
(assert (= -1 (ct-eval ctx "(cond-> 1 true (- 2) false inc)")) "cond-> mix"))
|
||||
(print " ok")
|
||||
|
||||
# --- cond->> ---
|
||||
(print "test cond->>...")
|
||||
(let [ctx (init)]
|
||||
(assert (= 0 (ct-eval ctx "(cond->> 0)")) "cond->> single")
|
||||
(assert (= 1 (ct-eval ctx "(cond->> 0 true inc true (- 2))")) "cond->> with tests")
|
||||
(assert (= 0 (ct-eval ctx "(cond->> 0 false inc)")) "cond->> false test")
|
||||
(assert (= 1 (ct-eval ctx "(cond->> 1 true (- 2) false inc)")) "cond->> mix"))
|
||||
(print " ok")
|
||||
|
||||
# --- as-> ---
|
||||
(print "test as->...")
|
||||
(let [ctx (init)]
|
||||
(assert (= 0 (ct-eval ctx "(as-> 0 x)")) "as-> single")
|
||||
(assert (= 1 (ct-eval ctx "(as-> 0 x (inc x))")) "as-> one form")
|
||||
(assert (= 2 (ct-eval ctx "(as-> [0 1] x (map inc x) (reverse x) (first x))")) "as-> chain"))
|
||||
(print " ok")
|
||||
|
||||
(print "\nAll Ported Macros tests passed!")
|
||||
|
|
@ -42,14 +42,15 @@
|
|||
(assert (= true (ct-eval ctx "(= [1 2 3] (distinct (lazy-seq [1 2 1 3 2])))")) "distinct lazy"))
|
||||
(print " ok")
|
||||
|
||||
(print "\n8: fib-seq via lazy-cat (self-referencing lazy-seq)...")
|
||||
(print "8: fib-seq (deferred — eager core-map needs lazy upgrade)...")
|
||||
(let [ctx (init)]
|
||||
(print " NOTE: self-referencing lazy-seqs currently trigger eager realization, causing infinite recursion.")
|
||||
(print " This is a known limitation — our lazy-seq model forces the entire thunk at once.")
|
||||
(print " Skipping fib-seq integration test for now.")
|
||||
(print " When fixed, the test should assert:")
|
||||
(print " (def fib-seq (lazy-cat [0 1] (map + (rest fib-seq) fib-seq)))")
|
||||
(print " (= [0 1 1 2 3 5 8 13 21 34] (take 10 fib-seq))"))
|
||||
(print " ok (deferred)")
|
||||
(print " The cell-by-cell lazy-seq architecture is correct. concat, take,")
|
||||
(print " drop, nth, first, rest all work with self-referencing patterns.")
|
||||
(print " core-map still eagerly realizes all lazy-seq arguments, which")
|
||||
(print " loops on infinite sequences. Making core-map return a lazy")
|
||||
(print " result would complete the self-referencing lazy-seq story.")
|
||||
(print " When fixed: (def fib-seq (lazy-cat [0 1] (map + (rest fib-seq) fib-seq)))")
|
||||
(print " → (take 10 fib-seq) = [0 1 1 2 3 5 8 13 21 34]"))
|
||||
(print " ok (deferred — core-map needs lazy upgrade)")
|
||||
|
||||
(print "\nAll LazySeq tests passed!")
|
||||
|
|
|
|||
100
test/logic-test.janet
Normal file
100
test/logic-test.janet
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
(use ../src/jolt/api)
|
||||
(defn ct-eval [ctx s] (eval-string ctx s))
|
||||
(print "Ported Logic Tests (from clojure/test-clojure/logic.clj)")
|
||||
|
||||
(print "1: test-if true/false/nil...")
|
||||
(let [ctx (init)]
|
||||
(assert (= :t (ct-eval ctx "(if true :t)")) "if true")
|
||||
(assert (= :t (ct-eval ctx "(if true :t :f)")) "if true with else")
|
||||
(assert (= nil (ct-eval ctx "(if false :t)")) "if false no else")
|
||||
(assert (= :f (ct-eval ctx "(if false :t :f)")) "if false with else")
|
||||
(assert (= nil (ct-eval ctx "(if nil :t)")) "if nil no else")
|
||||
(assert (= :f (ct-eval ctx "(if nil :t :f)")) "if nil with else"))
|
||||
(print " ok")
|
||||
|
||||
(print "2: test-if zero/empty is true...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx "(= :t (if 0 :t :f))")) "0 is true")
|
||||
(assert (= true (ct-eval ctx "(= :t (if 0.0 :t :f))")) "0.0 is true")
|
||||
(assert (= true (ct-eval ctx "(= :t (if \"\" :t :f))")) "empty string is true")
|
||||
(assert (= true (ct-eval ctx "(= :t (if () :t :f))")) "empty list is true")
|
||||
(assert (= true (ct-eval ctx "(= :t (if [] :t :f))")) "empty vector is true")
|
||||
(assert (= true (ct-eval ctx "(= :t (if {} :t :f))")) "empty map is true")
|
||||
(assert (= true (ct-eval ctx "(= :t (if #{} :t :f))")) "empty set is true"))
|
||||
(print " ok")
|
||||
|
||||
(print "3: test-if anything except nil/false is true...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx "(= :t (if 42 :t :f))")) "42 is true")
|
||||
(assert (= true (ct-eval ctx "(= :t (if 1.5 :t :f))")) "float is true")
|
||||
(assert (= true (ct-eval ctx "(= :t (if \"abc\" :t :f))")) "string is true")
|
||||
(assert (= true (ct-eval ctx "(= :t (if 'abc :t :f))")) "symbol is true")
|
||||
(assert (= true (ct-eval ctx "(= :t (if :kw :t :f))")) "keyword is true")
|
||||
(assert (= true (ct-eval ctx "(= :t (if '(1 2) :t :f))")) "list is true")
|
||||
(assert (= true (ct-eval ctx "(= :t (if [1 2] :t :f))")) "vector is true")
|
||||
(assert (= true (ct-eval ctx "(= :t (if {:a 1 :b 2} :t :f))")) "map is true")
|
||||
(assert (= true (ct-eval ctx "(= :t (if #{1 2} :t :f))")) "set is true"))
|
||||
(print " ok")
|
||||
|
||||
(print "4: test-nil-punning...")
|
||||
(let [ctx (init)]
|
||||
(assert (= :yes (ct-eval ctx "(if (first []) :no :yes)")) "first [] nil")
|
||||
(assert (= :yes (ct-eval ctx "(if (next [1]) :no :yes)")) "next [1] nil")
|
||||
(assert (= :no (ct-eval ctx "(if (rest [1]) :no :yes)")) "rest [1] non-nil")
|
||||
(assert (= :yes (ct-eval ctx "(if (seq nil) :no :yes)")) "seq nil")
|
||||
(assert (= :yes (ct-eval ctx "(if (seq []) :no :yes)")) "seq [] nil")
|
||||
(assert (= :no (ct-eval ctx "(if (lazy-seq nil) :no :yes)")) "lazy-seq nil non-nil")
|
||||
(assert (= :no (ct-eval ctx "(if (lazy-seq []) :no :yes)")) "lazy-seq [] non-nil"))
|
||||
(print " ok")
|
||||
|
||||
(print "5: test-and...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx "(and)")) "and empty")
|
||||
(assert (= true (ct-eval ctx "(and true)")) "and true")
|
||||
(assert (= nil (ct-eval ctx "(and nil)")) "and nil")
|
||||
(assert (= false (ct-eval ctx "(and false)")) "and false")
|
||||
(assert (ct-eval ctx "(= \"abc\" (and 1 true :kw 'abc \"abc\"))") "and chain last")
|
||||
(assert (= nil (ct-eval ctx "(and 1 true nil 'abc \"abc\")")) "and chain nil")
|
||||
(assert (= false (ct-eval ctx "(and 1 true 'abc \"abc\" false)")) "and chain false"))
|
||||
(print " ok")
|
||||
|
||||
(print "6: test-or...")
|
||||
(let [ctx (init)]
|
||||
(assert (= nil (ct-eval ctx "(or)")) "or empty")
|
||||
(assert (= true (ct-eval ctx "(or true)")) "or true")
|
||||
(assert (= nil (ct-eval ctx "(or nil)")) "or nil")
|
||||
(assert (= false (ct-eval ctx "(or false)")) "or false")
|
||||
(assert (= true (ct-eval ctx "(or nil false true)")) "or nil false true")
|
||||
(assert (= 1 (ct-eval ctx "(or nil false 1 2)")) "or picks first truthy")
|
||||
(assert (ct-eval ctx "(= \"abc\" (or nil false \"abc\" :kw))") "or picks string")
|
||||
(assert (= nil (ct-eval ctx "(or false nil)")) "or false nil -> nil")
|
||||
(assert (= false (ct-eval ctx "(or nil false)")) "or nil false -> false")
|
||||
(assert (= false (ct-eval ctx "(or nil nil nil false)")) "or chain to false")
|
||||
(assert (= true (ct-eval ctx "(or nil true false)")) "or nil true false"))
|
||||
(print " ok")
|
||||
|
||||
(print "7: test-not...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx "(not nil)")) "not nil")
|
||||
(assert (= true (ct-eval ctx "(not false)")) "not false")
|
||||
(assert (= false (ct-eval ctx "(not true)")) "not true")
|
||||
(assert (= false (ct-eval ctx "(not 0)")) "not 0")
|
||||
(assert (= false (ct-eval ctx "(not 42)")) "not 42")
|
||||
(assert (= false (ct-eval ctx "(not \"\")")) "not empty string")
|
||||
(assert (= false (ct-eval ctx "(not \"abc\")")) "not string")
|
||||
(assert (= false (ct-eval ctx "(not ())")) "not empty list")
|
||||
(assert (= false (ct-eval ctx "(not [])")) "not empty vector")
|
||||
(assert (= false (ct-eval ctx "(not {})")) "not empty map")
|
||||
(assert (= false (ct-eval ctx "(not #{})")) "not empty set"))
|
||||
(print " ok")
|
||||
|
||||
(print "8: test-some?...")
|
||||
(let [ctx (init)]
|
||||
(assert (= false (ct-eval ctx "(some? nil)")) "some? nil")
|
||||
(assert (= true (ct-eval ctx "(some? false)")) "some? false")
|
||||
(assert (= true (ct-eval ctx "(some? 0)")) "some? 0")
|
||||
(assert (= true (ct-eval ctx "(some? \"abc\")")) "some? string")
|
||||
(assert (= true (ct-eval ctx "(some? [])")) "some? empty vec"))
|
||||
(print " ok")
|
||||
|
||||
(print "\nAll Ported Logic tests passed!")
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
(let [ls (ct-eval ctx "(lazy-seq (cons 1 (lazy-seq (cons 2 nil))))")]
|
||||
(assert (not (nil? ls)) "lazy-seq returns non-nil")
|
||||
(assert (= 1 (ct-eval ctx "(first (lazy-seq (cons 1 nil)))")) "first of lazy"))
|
||||
(assert (= [1 2 3] (ct-eval ctx "(seq (lazy-seq [1 2 3]))")) "seq forces lazy")
|
||||
(assert (= true (ct-eval ctx "(= [1 2 3] (seq (lazy-seq [1 2 3])))")) "seq forces lazy")
|
||||
(eval-string ctx "(def counter (atom 0))")
|
||||
(def val (ct-eval ctx "(let [ls (lazy-seq (do (swap! counter inc) [1 2 3]))] (seq ls) (seq ls) @counter)"))
|
||||
(assert (= 1 val) "realized once"))
|
||||
|
|
|
|||
212
test/systematic-coverage.janet
Normal file
212
test/systematic-coverage.janet
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
# Systematic coverage tests for Clojure language features
|
||||
(use ../src/jolt/api)
|
||||
(defn ct-eval [ctx s] (eval-string ctx s))
|
||||
|
||||
(print "Systematic Coverage Tests")
|
||||
|
||||
# --- Type Predicates ---
|
||||
(print "1: type predicates...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx "(integer? 0)")) "integer? 0")
|
||||
(assert (= true (ct-eval ctx "(integer? 1)")) "integer? 1")
|
||||
(assert (= true (ct-eval ctx "(integer? -5)")) "integer? negative")
|
||||
(assert (= false (ct-eval ctx "(integer? 1.5)")) "integer? float")
|
||||
(assert (= false (ct-eval ctx "(integer? nil)")) "integer? nil")
|
||||
(assert (= false (ct-eval ctx "(integer? \"abc\")")) "integer? string")
|
||||
(assert (= true (ct-eval ctx "(boolean? true)")) "boolean? true")
|
||||
(assert (= true (ct-eval ctx "(boolean? false)")) "boolean? false")
|
||||
(assert (= false (ct-eval ctx "(boolean? 1)")) "boolean? falsy")
|
||||
(assert (= false (ct-eval ctx "(boolean? nil)")) "boolean? nil")
|
||||
(assert (= true (ct-eval ctx "(nil? nil)")) "nil? nil")
|
||||
(assert (= false (ct-eval ctx "(nil? false)")) "nil? false")
|
||||
(assert (= false (ct-eval ctx "(nil? 0)")) "nil? 0")
|
||||
(assert (= false (ct-eval ctx "(nil? [])")) "nil? empty vec")
|
||||
(assert (= false (ct-eval ctx "(some? nil)")) "some? nil")
|
||||
(assert (= true (ct-eval ctx "(some? false)")) "some? false")
|
||||
(assert (= true (ct-eval ctx "(some? 0)")) "some? 0")
|
||||
(assert (= true (ct-eval ctx "(some? [])")) "some? empty vec")
|
||||
(assert (= true (ct-eval ctx "(string? \"hello\")")) "string?")
|
||||
(assert (= false (ct-eval ctx "(string? 42)")) "string? false")
|
||||
(assert (= true (ct-eval ctx "(string? \"\")")) "string? empty")
|
||||
(assert (= true (ct-eval ctx "(number? 42)")) "number? int")
|
||||
(assert (= true (ct-eval ctx "(number? 3.14)")) "number? float")
|
||||
(assert (= false (ct-eval ctx "(number? \"42\")")) "number? string")
|
||||
(assert (= true (ct-eval ctx "(fn? inc)")) "fn? builtin")
|
||||
(assert (= false (ct-eval ctx "(fn? 42)")) "fn? false")
|
||||
(assert (= true (ct-eval ctx "(keyword? :foo)")) "keyword?")
|
||||
(assert (= false (ct-eval ctx "(keyword? \"foo\")")) "keyword? false")
|
||||
(assert (= true (ct-eval ctx "(symbol? 'abc)")) "symbol?")
|
||||
(assert (= false (ct-eval ctx "(symbol? :abc)")) "symbol? false")
|
||||
(assert (= true (ct-eval ctx "(map? {:a 1})")) "map?")
|
||||
(assert (= false (ct-eval ctx "(map? [1 2])")) "map? false")
|
||||
(assert (= true (ct-eval ctx "(set? #{1 2})")) "set?")
|
||||
(assert (= false (ct-eval ctx "(set? [1 2])")) "set? false")
|
||||
(assert (= true (ct-eval ctx "(seq? '(1 2))")) "seq? list")
|
||||
(assert (= true (ct-eval ctx "(seq? [1 2])")) "seq? vector")
|
||||
(assert (= true (ct-eval ctx "(coll? [1 2])")) "coll? vec")
|
||||
(assert (= true (ct-eval ctx "(coll? '(1 2))")) "coll? list")
|
||||
(assert (= true (ct-eval ctx "(coll? {})")) "coll? map")
|
||||
(assert (= true (ct-eval ctx "(true? true)")) "true? true")
|
||||
(assert (= false (ct-eval ctx "(true? 1)")) "true? 1")
|
||||
(assert (= true (ct-eval ctx "(false? false)")) "false? false")
|
||||
(assert (= false (ct-eval ctx "(false? nil)")) "false? nil")
|
||||
(assert (= true (ct-eval ctx "(identical? 42 42)")) "identical? same value"))
|
||||
(print " ok")
|
||||
|
||||
# --- Number Predicates ---
|
||||
(print "2: number predicates...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx "(zero? 0)")) "zero? 0")
|
||||
(assert (= false (ct-eval ctx "(zero? 1)")) "zero? 1")
|
||||
(assert (= false (ct-eval ctx "(zero? nil)")) "zero? nil")
|
||||
(assert (= true (ct-eval ctx "(pos? 1)")) "pos?")
|
||||
(assert (= false (ct-eval ctx "(pos? 0)")) "pos? 0")
|
||||
(assert (= false (ct-eval ctx "(pos? -1)")) "pos? -1")
|
||||
(assert (= true (ct-eval ctx "(neg? -1)")) "neg?")
|
||||
(assert (= false (ct-eval ctx "(neg? 0)")) "neg? 0")
|
||||
(assert (= false (ct-eval ctx "(neg? 1)")) "neg? 1")
|
||||
(assert (= true (ct-eval ctx "(even? 0)")) "even? 0")
|
||||
(assert (= true (ct-eval ctx "(even? 2)")) "even? 2")
|
||||
(assert (= false (ct-eval ctx "(even? 1)")) "even? 1")
|
||||
(assert (= true (ct-eval ctx "(odd? 1)")) "odd? 1")
|
||||
(assert (= true (ct-eval ctx "(odd? 3)")) "odd? 3")
|
||||
(assert (= false (ct-eval ctx "(odd? 2)")) "odd? 2"))
|
||||
(print " ok")
|
||||
|
||||
# --- Math ---
|
||||
(print "3: math operations...")
|
||||
(let [ctx (init)]
|
||||
(assert (= 0 (ct-eval ctx "(+)")) "+ 0 args")
|
||||
(assert (= 5 (ct-eval ctx "(+ 2 3)")) "+ 2 args")
|
||||
(assert (= 10 (ct-eval ctx "(+ 1 2 3 4)")) "+ varargs")
|
||||
(assert (= -5 (ct-eval ctx "(- 5)")) "- unary")
|
||||
(assert (= 2 (ct-eval ctx "(- 5 3)")) "- binary")
|
||||
(assert (= 1 (ct-eval ctx "(*)")) "* 0 args")
|
||||
(assert (= 6 (ct-eval ctx "(* 2 3)")) "*")
|
||||
(assert (= 0.5 (ct-eval ctx "(/ 2)")) "/ unary reciprocal")
|
||||
(assert (= 2 (ct-eval ctx "(/ 4 2)")) "/ binary")
|
||||
(assert (= 2 (ct-eval ctx "(quot 5 2)")) "quot")
|
||||
(assert (= 1 (ct-eval ctx "(rem 5 2)")) "rem")
|
||||
(assert (= 1 (ct-eval ctx "(mod 5 2)")) "mod")
|
||||
(assert (= 43 (ct-eval ctx "(inc 42)")) "inc")
|
||||
(assert (= 41 (ct-eval ctx "(dec 42)")) "dec")
|
||||
(assert (= 3 (ct-eval ctx "(max 1 2 3)")) "max")
|
||||
(assert (= 1 (ct-eval ctx "(min 1 2 3)")) "min")
|
||||
(assert (= 5 (ct-eval ctx "(abs -5)")) "abs negative")
|
||||
(assert (= 5 (ct-eval ctx "(abs 5)")) "abs positive")
|
||||
(assert (= 0 (ct-eval ctx "(abs 0)")) "abs 0")
|
||||
(assert (= 3.14 (ct-eval ctx "(abs -3.14)")) "abs float")
|
||||
(assert (= true (ct-eval ctx "(< (rand) 1)")) "rand < 1")
|
||||
(assert (= true (ct-eval ctx "(number? (rand-int 10))")) "rand-int type"))
|
||||
(print " ok")
|
||||
|
||||
# --- Comparison ---
|
||||
(print "4: comparison...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx "(= 1 1)")) "= same")
|
||||
(assert (= true (ct-eval ctx "(= 1 1 1)")) "= multi same")
|
||||
(assert (= false (ct-eval ctx "(= 1 2)")) "= different")
|
||||
(assert (= true (ct-eval ctx "(not= 1 2)")) "not= different")
|
||||
(assert (= false (ct-eval ctx "(not= 1 1)")) "not= same")
|
||||
(assert (= true (ct-eval ctx "(< 1 2)")) "<")
|
||||
(assert (= false (ct-eval ctx "(< 2 1)")) "< false")
|
||||
(assert (= true (ct-eval ctx "(> 2 1)")) ">")
|
||||
(assert (= true (ct-eval ctx "(<= 1 1)")) "<=")
|
||||
(assert (= true (ct-eval ctx "(>= 2 2)")) ">="))
|
||||
(print " ok")
|
||||
|
||||
# --- Boolean logic ---
|
||||
(print "5: boolean logic...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx "(and)")) "and empty")
|
||||
(assert (= true (ct-eval ctx "(and true)")) "and true")
|
||||
(assert (= nil (ct-eval ctx "(and nil)")) "and nil")
|
||||
(assert (= false (ct-eval ctx "(and false)")) "and false")
|
||||
(assert (= 3 (ct-eval ctx "(and 1 2 3)")) "and last")
|
||||
(assert (= nil (ct-eval ctx "(and 1 nil 3)")) "and short-circuit")
|
||||
(assert (= nil (ct-eval ctx "(or)")) "or empty")
|
||||
(assert (= true (ct-eval ctx "(or true)")) "or true")
|
||||
(assert (= nil (ct-eval ctx "(or nil)")) "or nil")
|
||||
(assert (= false (ct-eval ctx "(or false)")) "or false")
|
||||
(assert (= 1 (ct-eval ctx "(or nil false 1 2)")) "or first truthy")
|
||||
(assert (= false (ct-eval ctx "(or nil false)")) "or all falsy")
|
||||
(assert (= false (ct-eval ctx "(not true)")) "not true")
|
||||
(assert (= true (ct-eval ctx "(not nil)")) "not nil")
|
||||
(assert (= true (ct-eval ctx "(not false)")) "not false"))
|
||||
(print " ok")
|
||||
|
||||
# --- Collections ---
|
||||
(print "6: collections...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx "(empty? nil)")) "empty? nil")
|
||||
(assert (= true (ct-eval ctx "(empty? [])")) "empty? []")
|
||||
(assert (= true (ct-eval ctx "(empty? ())")) "empty? ()")
|
||||
(assert (= true (ct-eval ctx "(empty? {})")) "empty? {}")
|
||||
(assert (= true (ct-eval ctx "(empty? #{})")) "empty? #{}")
|
||||
(assert (= true (ct-eval ctx "(empty? \"\")")) "empty? empty string")
|
||||
(assert (= false (ct-eval ctx "(empty? [1])")) "empty? non-empty")
|
||||
(assert (= true (ct-eval ctx "(every? even? [2 4 6])")) "every? true")
|
||||
(assert (= false (ct-eval ctx "(every? even? [2 3 4])")) "every? false")
|
||||
(assert (= 3 (ct-eval ctx "(count [1 2 3])")) "count vector")
|
||||
(assert (= 2 (ct-eval ctx "(count {:a 1 :b 2})")) "count map")
|
||||
(assert (= 3 (ct-eval ctx "(count #{1 2 3})")) "count set")
|
||||
(assert (= 3 (ct-eval ctx "(count \"abc\")")) "count string"))
|
||||
(print " ok")
|
||||
|
||||
# --- Sequence Operations ---
|
||||
(print "7: sequence operations...")
|
||||
(let [ctx (init)]
|
||||
(assert (= 1 (ct-eval ctx "(first [1 2 3])")) "first")
|
||||
(assert (= nil (ct-eval ctx "(first [])")) "first empty")
|
||||
(assert (= nil (ct-eval ctx "(first nil)")) "first nil")
|
||||
(assert (= :a (ct-eval ctx "(nth [:a :b :c] 0)")) "nth 0")
|
||||
(assert (= :c (ct-eval ctx "(nth [:a :b :c] 2)")) "nth 2")
|
||||
(assert (= :d (ct-eval ctx "(nth [:a :b :c] 3 :d)")) "nth default")
|
||||
(assert (= nil (ct-eval ctx "(seq [])")) "seq empty -> nil")
|
||||
(assert (= nil (ct-eval ctx "(seq nil)")) "seq nil -> nil")
|
||||
(assert (= true (ct-eval ctx "(= [2 3 4] (map inc [1 2 3]))")) "map")
|
||||
(assert (= true (ct-eval ctx "(= [2 4] (filter even? [1 2 3 4]))")) "filter")
|
||||
(assert (= 6 (ct-eval ctx "(reduce + [1 2 3])")) "reduce")
|
||||
(assert (= 10 (ct-eval ctx "(reduce + 4 [1 2 3])")) "reduce init")
|
||||
(assert (= true (ct-eval ctx "(= [1 2] (take 2 [1 2 3 4]))")) "take")
|
||||
(assert (= true (ct-eval ctx "(= [3 4] (drop 2 [1 2 3 4]))")) "drop")
|
||||
(assert (= true (ct-eval ctx "(= [3 2 1] (reverse [1 2 3]))")) "reverse")
|
||||
(assert (= true (ct-eval ctx "(= 3 (count (distinct [1 1 2 2 3 3])))")) "distinct"))
|
||||
(print " ok")
|
||||
|
||||
# --- Collections: conj, assoc, dissoc, get ---
|
||||
(print "8: collection mutation...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx "(= [1 2 3] (conj [1 2] 3))")) "conj vector")
|
||||
(assert (= true (ct-eval ctx "(= (quote (0 1 2)) (conj (quote (1 2)) 0))")) "conj list prepend")
|
||||
(assert (= true (ct-eval ctx "(= {:a 1 :b 2} (assoc {:a 1} :b 2))")) "assoc")
|
||||
(assert (= true (ct-eval ctx "(= {:a 1} (dissoc {:a 1 :b 2} :b))")) "dissoc")
|
||||
(assert (= 1 (ct-eval ctx "(get {:a 1} :a)")) "get")
|
||||
(assert (= nil (ct-eval ctx "(get {:a 1} :z)")) "get missing")
|
||||
(assert (= :d (ct-eval ctx "(get {:a 1} :z :d)")) "get default")
|
||||
(assert (= true (ct-eval ctx "(contains? {:a 1} :a)")) "contains? map")
|
||||
(assert (= false (ct-eval ctx "(contains? {:a 1} :z)")) "contains? missing")
|
||||
(assert (= true (ct-eval ctx "(contains? [5 6 7] 1)")) "contains? vector")
|
||||
(assert (= false (ct-eval ctx "(contains? [5 6 7] 3)")) "contains? vector oob"))
|
||||
(print " ok")
|
||||
|
||||
# --- Higher-order functions ---
|
||||
(print "9: higher-order functions...")
|
||||
(let [ctx (init)]
|
||||
(assert (= 3 (ct-eval ctx "((comp inc inc) 1)")) "comp")
|
||||
(assert (= 42 (ct-eval ctx "(identity 42)")) "identity")
|
||||
(assert (= 99 (ct-eval ctx "((constantly 99) :anything)")) "constantly")
|
||||
(assert (= true (ct-eval ctx "(= 10 ((partial + 3 7)))")) "partial")
|
||||
(assert (= true (ct-eval ctx "((every-pred even? pos?) 4)")) "every-pred true")
|
||||
(assert (= false (ct-eval ctx "((every-pred even? pos?) -4)")) "every-pred false")
|
||||
(assert (= false (ct-eval ctx "((complement even?) 2)")) "complement"))
|
||||
(print " ok")
|
||||
|
||||
# --- Destructuring ---
|
||||
(print "10: destructuring...")
|
||||
(let [ctx (init)]
|
||||
(assert (= 1 (ct-eval ctx "(let [[x] [1 2 3]] x)")) "seq destructure first")
|
||||
(assert (= 2 (ct-eval ctx "(let [[_ y] [1 2 3]] y)")) "seq destructure second"))
|
||||
(print " ok")
|
||||
|
||||
(print "\nAll Systematic Coverage tests passed!")
|
||||
Loading…
Add table
Add a link
Reference in a new issue