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.
This commit is contained in:
parent
1898308926
commit
16428179fa
52 changed files with 185 additions and 86 deletions
335
test/unit/compiler-test.janet
Normal file
335
test/unit/compiler-test.janet
Normal file
|
|
@ -0,0 +1,335 @@
|
|||
# Jolt Compiler Tests — Phase 2
|
||||
# Tests for source-to-source Clojure→Janet compilation.
|
||||
# Core ops: const, do, if, def, fn, let, invoke
|
||||
# Phase 2 adds: symbol classification with binding awareness
|
||||
|
||||
(use ../../src/jolt/compiler)
|
||||
(use ../../src/jolt/reader)
|
||||
|
||||
(defn compile-str [s]
|
||||
(let [form (parse-string s)]
|
||||
(compile-form form)))
|
||||
|
||||
# ============================================================
|
||||
# 1. Literals (const)
|
||||
# ============================================================
|
||||
(print "1: literal constants...")
|
||||
(assert (= "42" (compile-str "42")) "integer")
|
||||
(assert (= "nil" (compile-str "nil")) "nil")
|
||||
(assert (= "true" (compile-str "true")) "true")
|
||||
(assert (= "false" (compile-str "false")) "false")
|
||||
(assert (= "\"hello\"" (compile-str "\"hello\"")) "string")
|
||||
(assert (= ":foo" (compile-str ":foo")) "keyword")
|
||||
(print " passed")
|
||||
|
||||
# ============================================================
|
||||
# 2. do
|
||||
# ============================================================
|
||||
(print "2: do...")
|
||||
(assert (= "(do 1 2)" (compile-str "(do 1 2)")) "do two exprs")
|
||||
(assert (= "(do 42)" (compile-str "(do 42)")) "do single expr")
|
||||
(assert (= "(do (core-inc 1) (core-inc 2))" (compile-str "(do (inc 1) (inc 2))")) "do with fn calls")
|
||||
(print " passed")
|
||||
|
||||
# ============================================================
|
||||
# 3. if
|
||||
# ============================================================
|
||||
(print "3: if...")
|
||||
(assert (= "(if true 1 2)" (compile-str "(if true 1 2)")) "if three-arg")
|
||||
(assert (= "(if false 1 nil)" (compile-str "(if false 1)")) "if two-arg")
|
||||
(print " passed")
|
||||
|
||||
# ============================================================
|
||||
# 4. def
|
||||
# ============================================================
|
||||
(print "4: def...")
|
||||
(assert (= "(def x 42)" (compile-str "(def x 42)")) "def constant")
|
||||
(assert (= "(def f (fn [x] (core-inc x)))" (compile-str "(def f (fn* [x] (inc x)))")) "def with fn")
|
||||
(print " passed")
|
||||
|
||||
# ============================================================
|
||||
# 5. fn
|
||||
# ============================================================
|
||||
(print "5: fn...")
|
||||
(assert (= "(fn [x] (core-inc x))" (compile-str "(fn* [x] (inc x))")) "fn single arity")
|
||||
(assert (= "(fn [] 42)" (compile-str "(fn* [] 42)")) "fn no args")
|
||||
(assert (= "(fn [x] (do (core-print x) (core-inc x)))"
|
||||
(compile-str "(fn* [x] (print x) (inc x))")) "fn multi-expr body")
|
||||
(print " passed")
|
||||
|
||||
# ============================================================
|
||||
# 6. let
|
||||
# ============================================================
|
||||
(print "6: let...")
|
||||
(assert (= "(let [x 1] (core-inc x))" (compile-str "(let* [x 1] (inc x))")) "let single binding")
|
||||
(assert (= "(let [x 1 y 2] (core-+ x y))" (compile-str "(let* [x 1 y 2] (+ x y))")) "let two bindings")
|
||||
(assert (= "(let [x (core-inc 1)] (core-inc x))" (compile-str "(let* [x (inc 1)] (inc x))")) "let with fn in binding")
|
||||
(print " passed")
|
||||
|
||||
# ============================================================
|
||||
# 7. invoke (function calls)
|
||||
# ============================================================
|
||||
(print "7: invoke...")
|
||||
(assert (= "(core-inc 1)" (compile-str "(inc 1)")) "inc call")
|
||||
(assert (= "(core-+ 1 2)" (compile-str "(+ 1 2)")) "+ call")
|
||||
(assert (= "(core-+ (core-inc 1) 2)" (compile-str "(+ (inc 1) 2)")) "nested calls")
|
||||
(assert (= "(core-map core-inc (core-vec 1 2 3))"
|
||||
(compile-str "(map inc (vec 1 2 3))")) "multi-arg call")
|
||||
(print " passed")
|
||||
|
||||
# ============================================================
|
||||
# 8. Local symbol classification (Phase 2)
|
||||
# ============================================================
|
||||
(print "8: local classification...")
|
||||
# Shadowing: local inc should NOT be rewritten to core-inc
|
||||
(assert (= "(let [inc 5] (inc inc))"
|
||||
(compile-str "(let* [inc 5] (inc inc))")) "local shadows core fn")
|
||||
# fn params are locals, not core symbols
|
||||
(assert (= "(fn [map] (core-vec map))"
|
||||
(compile-str "(fn* [map] (vec map))")) "fn param shadows core map")
|
||||
# nested let with shadowing
|
||||
(assert (= "(let [x 1] (let [inc x] (inc x)))"
|
||||
(compile-str "(let* [x 1] (let* [inc x] (inc x)))")) "nested let local")
|
||||
(print " passed")
|
||||
|
||||
(print "\nAll compiler Phase 2 tests passed!")
|
||||
|
||||
# ============================================================
|
||||
# 9. Compile-and-eval round-trip (Phase 3)
|
||||
# ============================================================
|
||||
(print "9: compile-and-eval...")
|
||||
(use ../../src/jolt/core) # need core fns in scope for eval
|
||||
|
||||
(defn compile-eval-str [s]
|
||||
(let [form (parse-string s)]
|
||||
(compile-and-eval form nil)))
|
||||
|
||||
(assert (= 42 (compile-eval-str "42")) "eval literal")
|
||||
(assert (= 2 (compile-eval-str "(inc 1)")) "eval inc")
|
||||
(assert (= 3 (compile-eval-str "(+ 1 2)")) "eval +")
|
||||
(assert (= 6 (compile-eval-str "(+ (inc 1) (inc 3))")) "eval nested")
|
||||
(assert (= 2 (compile-eval-str "(do 1 2)")) "eval do")
|
||||
(assert (= 1 (compile-eval-str "(if true 1 2)")) "eval if true")
|
||||
(assert (= 2 (compile-eval-str "(if false 1 2)")) "eval if false")
|
||||
(assert (= 2 (compile-eval-str "(let* [x 1] (inc x))")) "eval let")
|
||||
(let [f (compile-eval-str "(fn* [x] (inc x))")]
|
||||
(assert (function? f) "eval fn returns fn")
|
||||
(assert (= 6 (f 5)) "eval fn works"))
|
||||
(print " passed")
|
||||
|
||||
# ============================================================
|
||||
# 10. Compile flag in context (Phase 3)
|
||||
# ============================================================
|
||||
(print "10: compile flag...")
|
||||
(use ../../src/jolt/api)
|
||||
|
||||
# Without compile flag
|
||||
(let [ctx (init)]
|
||||
(assert (= 2 (eval-string ctx "(inc 1)")) "no-compile flag: inc works"))
|
||||
|
||||
# With compile flag: pure expressions use compile-and-eval
|
||||
(let [ctx (init {:compile? true})]
|
||||
(assert (= 2 (eval-string ctx "(inc 1)")) "compile flag: inc works")
|
||||
(assert (= 3 (eval-string ctx "(+ 1 2)")) "compile flag: + works")
|
||||
(assert (= 6 (eval-string ctx "(+ (inc 1) (inc 3))")) "compile flag: nested works"))
|
||||
|
||||
# With compile flag: stateful forms fall back to interpreter
|
||||
(let [ctx (init {:compile? true})]
|
||||
(eval-string ctx "(def foo 99)")
|
||||
(assert (= 99 (eval-string ctx "foo")) "compile flag: def works"))
|
||||
|
||||
(print " passed")
|
||||
|
||||
(print "\nAll compiler Phase 3 tests passed!")
|
||||
|
||||
# ============================================================
|
||||
# 11. Macro expansion (Phase 4)
|
||||
# ============================================================
|
||||
(print "11: macro expansion...")
|
||||
(use ../../src/jolt/api)
|
||||
|
||||
(let [ctx (init {:compile? true})]
|
||||
# defn expands via compiler, produces Janet def
|
||||
(eval-string ctx "(defn square [n] (* n n))")
|
||||
(assert (= 25 (eval-string ctx "(square 5)")) "defn via compiler")
|
||||
|
||||
# when macro
|
||||
(assert (= 42 (eval-string ctx "(when true 42)")) "when true")
|
||||
(assert (= nil (eval-string ctx "(when false 42)")) "when false")
|
||||
|
||||
# let macro
|
||||
(assert (= 30 (eval-string ctx "(let [x 10 y 20] (+ x y))")) "let macro")
|
||||
|
||||
# fn macro
|
||||
(assert (= 49 (eval-string ctx "((fn [x] (* x x)) 7)")) "fn macro")
|
||||
|
||||
# and/or
|
||||
(assert (= 3 (eval-string ctx "(and 1 2 3)")) "and")
|
||||
(assert (= 99 (eval-string ctx "(or nil false 99)")) "or"))
|
||||
|
||||
(print " passed")
|
||||
|
||||
(print "\nAll compiler Phase 4 tests passed!")
|
||||
|
||||
# ============================================================
|
||||
# 12. throw, try, loop*/recur (Phase 5)
|
||||
# ============================================================
|
||||
(print "12: throw/try/loop...")
|
||||
(use ../../src/jolt/api)
|
||||
|
||||
(let [ctx (init {:compile? true})]
|
||||
# throw/catch via compiler
|
||||
(assert (= "caught"
|
||||
(eval-string ctx "(try (throw 42) (catch Exception e \"caught\"))"))
|
||||
"try/catch")
|
||||
|
||||
# try without catch returns body
|
||||
(assert (= 1 (eval-string ctx "(try 1 (catch Exception e 2))")) "try no throw")
|
||||
|
||||
# throw in nested context
|
||||
(assert (= "ok"
|
||||
(eval-string ctx "(try (do (throw 99) 1) (catch Exception e \"ok\"))"))
|
||||
"throw in do")
|
||||
|
||||
# loop*/recur
|
||||
(assert (= 3 (eval-string ctx "(loop* [x 0] (if (< x 3) (recur (inc x)) x))"))
|
||||
"loop count up")
|
||||
(assert (= 3
|
||||
(eval-string ctx "(loop* [i 0 acc 0] (if (< i 3) (recur (inc i) (+ acc i)) acc))"))
|
||||
"loop with acc"))
|
||||
|
||||
(print " passed")
|
||||
|
||||
(print "\nAll compiler Phase 5 tests passed!")
|
||||
|
||||
# ============================================================
|
||||
# 13. defn/def integration (Phase 0 fix)
|
||||
# ============================================================
|
||||
(print "13: defn/def integration...")
|
||||
(use ../../src/jolt/api)
|
||||
|
||||
(let [ctx (init {:compile? true})]
|
||||
# defn produces a resolvable var
|
||||
(eval-string ctx "(defn identity-fn [x] x)")
|
||||
(assert (= 1 (eval-string ctx "(identity-fn 1)")) "defn works")
|
||||
(let [f (eval-string ctx "identity-fn")]
|
||||
(assert (function? f) "bare defn symbol returns fn"))
|
||||
|
||||
# def produces a resolvable var
|
||||
(eval-string ctx "(def answer 42)")
|
||||
(assert (= 42 (eval-string ctx "answer")) "def bare symbol")
|
||||
(assert (= 43 (eval-string ctx "(inc answer)")) "def in call"))
|
||||
|
||||
(print " passed")
|
||||
|
||||
(print "\nAll compiler tests passed!")
|
||||
|
||||
# ============================================================
|
||||
# 14. Phase 1: ns accessors + ns form extensions
|
||||
# ============================================================
|
||||
(print "14: ns accessors...")
|
||||
(use ../../src/jolt/api)
|
||||
|
||||
(let [ctx (init)]
|
||||
(eval-string ctx "(ns mytest.core)")
|
||||
(def ns-list (eval-string ctx "(all-ns)"))
|
||||
(assert (> (length ns-list) 0) "all-ns returns namespaces")
|
||||
# create-ns
|
||||
(eval-string ctx "(create-ns mytest.extra)")
|
||||
(def all (eval-string ctx "(all-ns)"))
|
||||
(assert (> (length all) 1) "create-ns adds namespace"))
|
||||
|
||||
(print " passed")
|
||||
|
||||
(print "15: ns form extensions...")
|
||||
(let [ctx (init)]
|
||||
# ns with :require + :refer
|
||||
(eval-string ctx "(ns test.ns-ext (:require [clojure.core :refer [inc +]]))")
|
||||
(assert (= 2 (eval-string ctx "(inc 1)")) "refer inc works"))
|
||||
|
||||
(print " passed")
|
||||
|
||||
(print "\nAll Phase 1 tests passed!")
|
||||
|
||||
# ============================================================
|
||||
# 17. Phase 3: Var system completion
|
||||
# ============================================================
|
||||
(print "17: var system...")
|
||||
(use ../../src/jolt/api)
|
||||
|
||||
(let [ctx (init)]
|
||||
(eval-string ctx "(def x-var-test 42)")
|
||||
(assert (= true (eval-string ctx "(var? (var x-var-test))")) "var?")
|
||||
(eval-string ctx "(def y-var-test 99)")
|
||||
(assert (= 99 (eval-string ctx "(var-get (var y-var-test))")) "var-get")
|
||||
(eval-string ctx "(def z-var-test 10)")
|
||||
(assert (= 20 (eval-string ctx "(do (var-set (var z-var-test) 20) (var-get (var z-var-test)))")) "var-set")
|
||||
(eval-string ctx "(def a-var-test 1)")
|
||||
(assert (= 2 (eval-string ctx "(do (alter-var-root (var a-var-test) inc) (var-get (var a-var-test)))")) "alter-var-root")
|
||||
(eval-string ctx "(def fv-var-test :found)")
|
||||
(assert (= :found (eval-string ctx "(var-get (find-var 'fv-var-test))")) "find-var")
|
||||
(eval-string ctx "(intern (the-ns) 'iv-var-test 77)")
|
||||
(assert (= 77 (eval-string ctx "iv-var-test")) "intern")
|
||||
(eval-string ctx "(def ^:dynamic *dv* 1)")
|
||||
(assert (= 99 (eval-string ctx "(binding [*dv* 99] *dv*)")) "dynamic binding"))
|
||||
(print " passed")
|
||||
|
||||
# ============================================================
|
||||
# 18. Phase 3: Var metadata
|
||||
# ============================================================
|
||||
(print "18: var metadata...")
|
||||
(let [ctx (init)]
|
||||
(eval-string ctx "(def mvar 42)")
|
||||
(eval-string ctx "(alter-meta! (var mvar) assoc :doc \"the answer\")")
|
||||
(assert (= "the answer" (eval-string ctx "(:doc (meta (var mvar)))")) "alter-meta!")
|
||||
(eval-string ctx "(reset-meta! (var mvar) {:a 1})")
|
||||
(assert (= 1 (eval-string ctx "(:a (meta (var mvar)))")) "reset-meta!"))
|
||||
(print " passed")
|
||||
|
||||
(print "\nAll Phase 3 tests passed!")
|
||||
|
||||
# ============================================================
|
||||
# 19. Phase 4: deftype
|
||||
# ============================================================
|
||||
(print "19: deftype...")
|
||||
(use ../../src/jolt/api)
|
||||
|
||||
(let [ctx (init)]
|
||||
(eval-string ctx "(deftype Point [x y])")
|
||||
(assert (not (nil? (eval-string ctx "(Point. 3 4)"))) "deftype constructs")
|
||||
(assert (= 3 (eval-string ctx "(. (Point. 3 4) x)")) ".x access")
|
||||
(assert (= 4 (eval-string ctx "(. (Point. 3 4) y)")) ".y access")
|
||||
(assert (= 10 (eval-string ctx "(let [p (Point. 3 4)] (set! (.-x p) 10) (. p x))")) "set! field")
|
||||
(assert (= true (eval-string ctx "(instance? Point (Point. 1 2))")) "instance?")
|
||||
(assert (= false (eval-string ctx "(instance? Point {:x 1})")) "instance? false")
|
||||
(assert (= 5 (eval-string ctx "(. (->Point 5 6) x)")) "arrow factory"))
|
||||
(print " passed")
|
||||
|
||||
# ============================================================
|
||||
# 20. Phase 4: defrecord
|
||||
# ============================================================
|
||||
(print "20: defrecord...")
|
||||
(let [ctx (init)]
|
||||
(eval-string ctx "(defrecord Person [name age])")
|
||||
(assert (not (nil? (eval-string ctx "(Person. \"Alice\" 30)"))) "record constructs")
|
||||
(assert (= true (eval-string ctx "(map? (Person. \"Alice\" 30))")) "record is map?")
|
||||
(assert (= "Alice" (eval-string ctx "(:name (Person. \"Alice\" 30))")) "keyword access")
|
||||
(assert (= 30 (eval-string ctx "(get (Person. \"Alice\" 30) :age)")) "get access")
|
||||
(assert (= "Alice" (eval-string ctx "(. (Person. \"Alice\" 30) name)")) ".name access")
|
||||
(assert (= 30 (eval-string ctx "(. (Person. \"Alice\" 30) age)")) ".age access")
|
||||
(assert (= 2 (eval-string ctx "(count (Person. \"Alice\" 30))")) "count")
|
||||
(assert (= "Bob" (eval-string ctx "(:name (->Person \"Bob\" 25))")) "arrow factory"))
|
||||
|
||||
(print " passed")
|
||||
|
||||
# ============================================================
|
||||
# 21. Phase 4: record equality
|
||||
# ============================================================
|
||||
(print "21: record equality...")
|
||||
(let [ctx (init)]
|
||||
(eval-string ctx "(defrecord Point3D [x y z])")
|
||||
(assert (= true (eval-string ctx "(= (Point3D. 1 2 3) (Point3D. 1 2 3))")) "same values")
|
||||
(assert (= false (eval-string ctx "(= (Point3D. 1 2 3) (Point3D. 4 5 6))")) "different values"))
|
||||
(print " passed")
|
||||
|
||||
(print "\nAll Phase 4 tests passed!")
|
||||
14
test/unit/eval-test.janet
Normal file
14
test/unit/eval-test.janet
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
(use ../../src/jolt/api)
|
||||
(defn ct-eval [ctx s] (normalize-pvecs (eval-string ctx s)))
|
||||
(print "Eval Tests")
|
||||
|
||||
(print "1: eval literal...")
|
||||
(let [ctx (init)]
|
||||
(assert (= 42 (ct-eval ctx "(eval 42)")) "eval literal")
|
||||
(assert (= 3 (ct-eval ctx "(eval '(+ 1 2))")) "eval quoted form")
|
||||
(assert (= 3 (ct-eval ctx "(eval (eval '(+ 1 2)))")) "eval nested")
|
||||
(ct-eval ctx "(eval '(def ex 99))")
|
||||
(assert (= 99 (ct-eval ctx "ex")) "eval defines var"))
|
||||
(print " ok")
|
||||
|
||||
(print "\nAll Eval tests passed!")
|
||||
151
test/unit/evaluator-test.janet
Normal file
151
test/unit/evaluator-test.janet
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
(use ../../src/jolt/evaluator)
|
||||
(use ../../src/jolt/types)
|
||||
(use ../../src/jolt/reader)
|
||||
(use ../../src/jolt/api)
|
||||
|
||||
# Helper: create a Jolt symbol
|
||||
(defn sym [name]
|
||||
(let [slash (string/find "/" name)]
|
||||
(if slash
|
||||
{:jolt/type :symbol :ns (string/slice name 0 slash)
|
||||
:name (string/slice name (+ slash 1))}
|
||||
{:jolt/type :symbol :ns nil :name name})))
|
||||
|
||||
# Helper: parse and eval
|
||||
(defn eval-str [s]
|
||||
(let [ctx (make-ctx)
|
||||
form (parse-string s)]
|
||||
(eval-form ctx @{} form)))
|
||||
|
||||
(print "1: literals...")
|
||||
(assert (= 42 (eval-str "42")) "integer")
|
||||
(assert (= "hello" (eval-str "\"hello\"")) "string")
|
||||
(assert (= true (eval-str "true")) "true")
|
||||
(assert (= false (eval-str "false")) "false")
|
||||
(assert (= nil (eval-str "nil")) "nil")
|
||||
(print " passed")
|
||||
|
||||
(print "2: quote...")
|
||||
(assert (deep= (sym "x") (eval-str "'x")) "quote returns symbol")
|
||||
(assert (deep= @[1 2 3] (eval-str "'(1 2 3)")) "quote list")
|
||||
(print " passed")
|
||||
|
||||
(print "3: do...")
|
||||
(assert (= 2 (eval-str "(do 1 2)")) "do returns last")
|
||||
(print " passed")
|
||||
|
||||
(print "4: if...")
|
||||
(assert (= 1 (eval-str "(if true 1 2)")) "if true")
|
||||
(assert (= 2 (eval-str "(if false 1 2)")) "if false")
|
||||
(assert (= :b (eval-str "(if nil :a :b)")) "if nil = false")
|
||||
(assert (= nil (eval-str "(if false 1)")) "if with no else")
|
||||
(print " passed")
|
||||
|
||||
(print "5: def...")
|
||||
(assert (= 42 (eval-str "(do (def x 42) x)")) "def in do")
|
||||
(print " passed")
|
||||
|
||||
(print "6: fn*...")
|
||||
(let [f (eval-str "(fn* [x] (inc x))")]
|
||||
(assert (function? f) "fn* returns function")
|
||||
(assert (= 42 (f 41)) "fn* fn works"))
|
||||
# nested function
|
||||
(let [f (eval-str "(fn* [x] (inc (inc x)))")]
|
||||
(assert (= 43 (f 41)) "nested inc"))
|
||||
(print " passed")
|
||||
|
||||
(print "7: let*...")
|
||||
(assert (= 2 (eval-str "(let* [x 1 y 2] y)")) "let* binds")
|
||||
(assert (= 3 (eval-str "(let* [x 1] (inc (inc x)))")) "let* with expr")
|
||||
(print " passed")
|
||||
|
||||
(print "8: loop*/recur...")
|
||||
(assert (= 5 (eval-str "(loop* [x 0] (if (< x 5) (recur (inc x)) x))"))
|
||||
"loop counts up")
|
||||
(assert (= 10 (eval-str "(loop* [i 0 acc 0] (if (< i 5) (recur (inc i) (+ acc i)) acc))"))
|
||||
"loop with multiple bindings")
|
||||
(print " passed")
|
||||
|
||||
(print "9: recur in fn*...")
|
||||
(let [countdown (eval-str "(fn* [n] (if (< n 1) 0 (recur (dec n))))")]
|
||||
(assert (= 0 (countdown 5)) "recur in fn"))
|
||||
(print " passed")
|
||||
|
||||
(print "10: throw/try/catch/finally...")
|
||||
# throw + catch
|
||||
(let [result (eval-str "(try (throw \"boom\") (catch Exception e \"caught\"))")]
|
||||
(assert (= "caught" result) "catch catches throw"))
|
||||
|
||||
# try with finally — body returns, finally runs
|
||||
(let [result (eval-str "(try 1 (finally 2))")]
|
||||
(assert (= 1 result) "try returns body even with finally"))
|
||||
|
||||
# try/catch/finally — catch returns, finally runs
|
||||
(let [result (eval-str "(try (throw \"err\") (catch Exception e \"handled\") (finally :cleanup))")]
|
||||
(assert (= "handled" result) "catch + finally returns catch value"))
|
||||
(print " passed")
|
||||
|
||||
(print "11: set!...")
|
||||
# set! on a var
|
||||
(assert (= 99 (eval-str "(do (def x 1) (set! x 99) x)")) "set! on var")
|
||||
# set! re-evaluates
|
||||
(assert (= 3 (eval-str "(do (def a 1) (def b 2) (set! a (+ a b)) a)")) "set! with expression")
|
||||
(print " passed")
|
||||
|
||||
(print "12: var...")
|
||||
# (var x) returns the var itself, not its value
|
||||
(let [v (eval-str "(do (def x 42) (var x))")]
|
||||
(assert (var? v) "(var x) returns a var")
|
||||
(assert (= 42 (var-get v)) "var holds value"))
|
||||
(print " passed")
|
||||
|
||||
(print "13: locking...")
|
||||
# locking is a no-op in single-threaded Janet — just executes body
|
||||
(assert (= 42 (eval-str "(locking :lock 42)")) "locking returns body result")
|
||||
(print " passed")
|
||||
|
||||
(print "14: instance?...")
|
||||
# instance? checks type
|
||||
(assert (= true (eval-str "(instance? Number 42)")) "instance? Number matches number")
|
||||
(assert (= false (eval-str "(instance? Number \"hello\")")) "instance? Number doesn't match string")
|
||||
(print " passed")
|
||||
|
||||
(print "15: defmulti/defmethod...")
|
||||
(let [ctx (make-ctx)]
|
||||
(eval-form ctx @{} (parse-string "(defmulti my-dispatch (fn* [x] (x :type)))"))
|
||||
(eval-form ctx @{} (parse-string "(defmethod my-dispatch :foo [_] :got-foo)"))
|
||||
(eval-form ctx @{} (parse-string "(defmethod my-dispatch :bar [_] :got-bar)"))
|
||||
(assert (= :got-foo (eval-form ctx @{} (parse-string "(my-dispatch {:type :foo})"))) "defmethod :foo dispatches")
|
||||
(assert (= :got-bar (eval-form ctx @{} (parse-string "(my-dispatch {:type :bar})"))) "defmethod :bar dispatches"))
|
||||
(print " passed")
|
||||
|
||||
(print "16: deftype...")
|
||||
(let [ctx (make-ctx)
|
||||
_ (eval-form ctx @{} (parse-string "(deftype Point [x y])"))
|
||||
_ (eval-form ctx @{} (parse-string "(def p (Point. 10 20))"))
|
||||
p-val (eval-form ctx @{} (parse-string "p"))
|
||||
x-val (eval-form ctx @{} (parse-string "(p :x)"))
|
||||
y-val (eval-form ctx @{} (parse-string "(p :y)"))
|
||||
result [x-val y-val]]
|
||||
(printf " p-val: %q" p-val)
|
||||
(printf " x-val: %q, y-val: %q" x-val y-val)
|
||||
(printf " result: %q" result)
|
||||
(assert (deep= [10 20] result) "deftype creates tagged instances with fields"))
|
||||
(print " passed")
|
||||
|
||||
(print "17: defmacro...")
|
||||
# define a macro using defmacro special form
|
||||
# init loads clojure.core so `list` is available
|
||||
(let [ctx (init)
|
||||
_ (eval-form ctx @{} (parse-string "(defmacro my-when [test body] (list 'if test body nil))"))
|
||||
result (eval-form ctx @{} (parse-string "(my-when true 2)"))]
|
||||
(assert (= 2 result) "defmacro defines callable macro"))
|
||||
# verify the var is marked :macro
|
||||
(let [ctx (make-ctx)
|
||||
_ (eval-form ctx @{} (parse-string "(defmacro m [x] (list 'quote x))"))
|
||||
v (resolve-var ctx @{} (parse-string "m"))]
|
||||
(assert v "macro var exists")
|
||||
(assert (v :macro) "macro var has :macro true"))
|
||||
(print " passed")
|
||||
|
||||
(print "\nAll evaluator tests passed!")
|
||||
46
test/unit/interop-test.janet
Normal file
46
test/unit/interop-test.janet
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
(use ../../src/jolt/api)
|
||||
(defn ct-eval [ctx s] (normalize-pvecs (eval-string ctx s)))
|
||||
(print "Janet Interop Tests")
|
||||
|
||||
(print "1: field access on tables...")
|
||||
(let [ctx (init)]
|
||||
(ct-eval ctx "(def t {:a 1 :b 2})")
|
||||
(assert (= 1 (ct-eval ctx "(. t :a)")) ". field access table")
|
||||
(assert (= 2 (ct-eval ctx "(. t :b)")) ". field access table 2")
|
||||
(assert (= nil (ct-eval ctx "(. t :z)")) ". field access missing key"))
|
||||
(print " ok")
|
||||
|
||||
(print "2: field access on structs...")
|
||||
(let [ctx (init)]
|
||||
(ct-eval ctx "(def s {:x 10 :y 20})")
|
||||
(assert (= 10 (ct-eval ctx "(. s :x)")) ". field access struct")
|
||||
(assert (= 20 (ct-eval ctx "(. s :y)")) ". field access struct 2"))
|
||||
(print " ok")
|
||||
|
||||
(print "3: method calls on tables...")
|
||||
(let [ctx (init)]
|
||||
(ct-eval ctx "(def obj {:greet (fn [self name] (str \"Hello \" name))})")
|
||||
(assert (= "Hello Alice" (ct-eval ctx "(. obj greet \"Alice\")")) ". method call on table"))
|
||||
(print " ok")
|
||||
|
||||
(print "4: field access via .- reader sugar...")
|
||||
(let [ctx (init)]
|
||||
(ct-eval ctx "(def t {:x 42 :len 5})")
|
||||
(assert (= 42 (ct-eval ctx "(.-x t)")) ".-x reader sugar")
|
||||
(assert (= 5 (ct-eval ctx "(.-len t)")) ".-len reader sugar"))
|
||||
(print " ok")
|
||||
|
||||
(print "5: . field access still works on deftypes...")
|
||||
(let [ctx (init)]
|
||||
(ct-eval ctx "(deftype Point [x y])")
|
||||
(assert (= 3 (ct-eval ctx "(. (Point. 3 4) x)")) ". field access deftype"))
|
||||
(print " ok")
|
||||
|
||||
(print "6: method call with multiple args...")
|
||||
(let [ctx (init)]
|
||||
(ct-eval ctx "(def calc {:add (fn [_ a b] (+ a b)) :mul (fn [_ a b] (* a b))})")
|
||||
(assert (= 7 (ct-eval ctx "(. calc add 3 4)")) ". method add")
|
||||
(assert (= 12 (ct-eval ctx "(. calc mul 3 4)")) ". method mul"))
|
||||
(print " ok")
|
||||
|
||||
(print "\nAll Janet Interop tests passed!")
|
||||
56
test/unit/lazy-seq-test.janet
Normal file
56
test/unit/lazy-seq-test.janet
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
(use ../../src/jolt/api)
|
||||
(defn ct-eval [ctx s] (normalize-pvecs (eval-string ctx s)))
|
||||
(print "LazySeq Tests")
|
||||
|
||||
(print "1: lazy-seq from list...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx "(= [1 2 3] (take 10 (lazy-seq [1 2 3])))")) "lazy-seq list")
|
||||
(assert (= true (ct-eval ctx "(= 3 (count (lazy-seq [1 2 3])))")) "count lazy-seq"))
|
||||
(print " ok")
|
||||
|
||||
(print "2: lazy-cat concatenation...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx "(= [1 2 3 4] (take 10 (lazy-cat [1 2] [3 4])))")) "lazy-cat concat")
|
||||
(assert (= true (ct-eval ctx "(= 4 (count (lazy-cat [1 2] [3 4])))")) "lazy-cat count"))
|
||||
(print " ok")
|
||||
|
||||
(print "3: first/rest on lazy-seqs...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx "(= 1 (first (lazy-seq [1 2 3])))")) "first lazy")
|
||||
(assert (= true (ct-eval ctx "(= 2 (first (rest (lazy-seq [1 2 3]))))")) "first rest lazy"))
|
||||
(print " ok")
|
||||
|
||||
(print "4: drop/nth on lazy-seqs...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx "(= [3 4 5] (take 10 (drop 2 (lazy-seq [1 2 3 4 5]))))")) "drop 2 take 10")
|
||||
(assert (= true (ct-eval ctx "(= 3 (nth (lazy-seq [1 2 3 4 5]) 2))")) "nth lazy"))
|
||||
(print " ok")
|
||||
|
||||
(print "5: concat on lazy-seqs...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx "(= 5 (count (concat (lazy-seq [1 2]) (lazy-seq [3 4 5]))))")) "concat lazy"))
|
||||
(print " ok")
|
||||
|
||||
(print "6: reverse/sort on lazy-seqs...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx "(= [3 2 1] (reverse (lazy-seq [1 2 3])))")) "reverse lazy")
|
||||
(assert (= true (ct-eval ctx "(= [1 2 3] (sort (lazy-seq [3 1 2])))")) "sort lazy"))
|
||||
(print " ok")
|
||||
|
||||
(print "7: distinct on lazy-seqs...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx "(= [1 2 3] (distinct (lazy-seq [1 2 1 3 2])))")) "distinct lazy"))
|
||||
(print " ok")
|
||||
|
||||
(print "8: fib-seq (deferred — eager core-map needs lazy upgrade)...")
|
||||
(let [ctx (init)]
|
||||
(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!")
|
||||
129
test/unit/macro-test.janet
Normal file
129
test/unit/macro-test.janet
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
(use ../../src/jolt/reader)
|
||||
(use ../../src/jolt/types)
|
||||
(use ../../src/jolt/evaluator)
|
||||
|
||||
# Helper: create a Jolt symbol
|
||||
(defn sym [name]
|
||||
(let [slash (string/find "/" name)]
|
||||
(if slash
|
||||
{:jolt/type :symbol :ns (string/slice name 0 slash)
|
||||
:name (string/slice name (+ slash 1))}
|
||||
{:jolt/type :symbol :ns nil :name name})))
|
||||
|
||||
# Helper: parse and eval
|
||||
(defn eval-str [s]
|
||||
(let [ctx (make-ctx)
|
||||
form (parse-string s)]
|
||||
(eval-form ctx @{} form)))
|
||||
|
||||
# ============================================================
|
||||
# 1. syntax-quote — literals pass through
|
||||
# ============================================================
|
||||
(print "1: syntax-quote literals...")
|
||||
(assert (= 42 (eval-str "`42")) "syntax-quote number")
|
||||
(assert (= "hello" (eval-str "`\"hello\"")) "syntax-quote string")
|
||||
(assert (= :foo (eval-str "`:foo")) "syntax-quote keyword")
|
||||
(assert (= nil (eval-str "`nil")) "syntax-quote nil")
|
||||
(assert (= true (eval-str "`true")) "syntax-quote true")
|
||||
(assert (= false (eval-str "`false")) "syntax-quote false")
|
||||
(print " passed")
|
||||
|
||||
# ============================================================
|
||||
# 2. syntax-quote — qualify symbols
|
||||
# ============================================================
|
||||
(print "2: syntax-quote qualifies symbols...")
|
||||
# In the 'user namespace, `x → user/x
|
||||
(let [form (eval-str "`x")]
|
||||
(assert (deep= {:jolt/type :symbol :ns "user" :name "x"} form)
|
||||
"qualifies bare symbol to current ns"))
|
||||
|
||||
# Already qualified symbols stay as-is
|
||||
(let [form (eval-str "`foo/bar")]
|
||||
(assert (deep= {:jolt/type :symbol :ns "foo" :name "bar"} form)
|
||||
"qualified symbol unchanged"))
|
||||
(print " passed")
|
||||
|
||||
# ============================================================
|
||||
# 3. syntax-quote — lists: qualify symbols, literal items as-is
|
||||
# ============================================================
|
||||
(print "3: syntax-quote lists...")
|
||||
# `(+ 1 2) → (user/+ 1 2) — but 1 and 2 are numbers, stay as-is
|
||||
(let [form (eval-str "`(+ 1 2)")]
|
||||
(assert (array? form) "syntax-quote list is array")
|
||||
(assert (= 3 (length form)) "has 3 elements")
|
||||
(assert (deep= {:jolt/type :symbol :ns "user" :name "+"} (in form 0))
|
||||
"operator qualified")
|
||||
(assert (= 1 (in form 1)) "number stays")
|
||||
(assert (= 2 (in form 2)) "number stays"))
|
||||
(print " passed")
|
||||
|
||||
# ============================================================
|
||||
# 4. unquote inside syntax-quote
|
||||
# ============================================================
|
||||
(print "4: unquote...")
|
||||
# `~x inside (let* [x 10] ...) → 10
|
||||
(let [form (eval-str "(let* [x 10] `~x)")]
|
||||
(assert (= 10 form) "unquote evaluates"))
|
||||
# `(~x) produces a list containing x
|
||||
(let [form2 (eval-str "(let* [x 10] `(~x))")]
|
||||
(assert (deep= @[10] form2) "unquote in list"))
|
||||
|
||||
# `(+ ~x ~y) with x=1 y=2 → (+ 1 2)
|
||||
(let [form (eval-str "(let* [x 1 y 2] `(+ ~x ~y))")]
|
||||
(assert (array? form) "result is list")
|
||||
(assert (= 1 (in form 1)) "first unquoted value")
|
||||
(assert (= 2 (in form 2)) "second unquoted value"))
|
||||
(print " passed")
|
||||
|
||||
# ============================================================
|
||||
# 5. unquote-splicing inside syntax-quote
|
||||
# ============================================================
|
||||
(print "5: unquote-splicing...")
|
||||
# `[1 2 ~@xs] with xs = (3 4) → [1 2 3 4]
|
||||
(let [form (eval-str "(let* [xs '(3 4)] `[1 2 ~@xs])")]
|
||||
(assert (tuple? form) "result is vector")
|
||||
(assert (= 4 (length form)) "spliced items merged")
|
||||
(assert (deep= [1 2 3 4] form) "correct items"))
|
||||
|
||||
# `(1 ~@xs) with xs = (2 3) → (1 2 3)
|
||||
(let [form (eval-str "(let* [xs '(2 3)] `(1 ~@xs))")]
|
||||
(assert (array? form) "result is list")
|
||||
(assert (= 3 (length form)) "spliced into list")
|
||||
(assert (deep= @[1 2 3] form) "correct items"))
|
||||
(print " passed")
|
||||
|
||||
# ============================================================
|
||||
# 6. Macro function application
|
||||
# ============================================================
|
||||
(print "6: macro application...")
|
||||
|
||||
# Define a simple macro: (my-when test body) → (if test body nil)
|
||||
(def ctx (make-ctx))
|
||||
(def macro-fn-form (parse-string "(fn* [test body] (if test body nil))"))
|
||||
(def macro-fn (eval-form ctx @{} macro-fn-form))
|
||||
# intern it in user namespace with string key
|
||||
(let [ns (ctx-find-ns ctx "user")]
|
||||
(ns-intern ns "my-when" macro-fn)
|
||||
(put (ns-find ns "my-when") :macro true))
|
||||
|
||||
# (my-when true 42) should expand to (if true 42 nil), evaluating to 42
|
||||
(let [form (parse-string "(my-when true 42)")]
|
||||
(assert (= 42 (eval-form ctx @{} form)) "macro application returns correct value"))
|
||||
|
||||
# (my-when false 99) should expand to (if false 99 nil), evaluating to nil
|
||||
(let [form (parse-string "(my-when false 99)")]
|
||||
(assert (= nil (eval-form ctx @{} form)) "macro returns nil when false"))
|
||||
(print " passed")
|
||||
|
||||
# ============================================================
|
||||
# 7. Nested syntax-quote
|
||||
# ============================================================
|
||||
(print "7: nested syntax-quote...")
|
||||
# ``x → (syntax-quote user/x)
|
||||
(let [form (eval-str "``x")]
|
||||
(assert (array? form) "nested syntax-quote produces list")
|
||||
(assert (deep= {:jolt/type :symbol :ns nil :name "syntax-quote"} (in form 0))
|
||||
"outer is syntax-quote"))
|
||||
(print " passed")
|
||||
|
||||
(print "\nAll macro tests passed!")
|
||||
75
test/unit/persistent-map-test.janet
Normal file
75
test/unit/persistent-map-test.janet
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
# Phase 2: PersistentHashMap Tests
|
||||
# Uses Clojure = (core-=) for PHM-aware comparison
|
||||
|
||||
(use ../../src/jolt/api)
|
||||
|
||||
(defn ct-eval [ctx s] (normalize-pvecs (eval-string ctx s)))
|
||||
|
||||
# Helper: compare via Clojure = which handles PHM
|
||||
(defn clj= [ctx a b]
|
||||
(eval-string ctx (string "(= " a " " b ")")))
|
||||
|
||||
# ============================================================
|
||||
# 1. Basic hash-map construction and access
|
||||
# ============================================================
|
||||
(print "1: hash-map construction...")
|
||||
(let [ctx (init)]
|
||||
(def m1 (ct-eval ctx "(hash-map :a 1)"))
|
||||
(assert (not (nil? m1)) "hash-map returns non-nil")
|
||||
(assert (= true (ct-eval ctx "(map? (hash-map :a 1))")) "map? returns true for PHM")
|
||||
(assert (= true (ct-eval ctx "(= (hash-map :a 1) {:a 1})")) "PHM = struct via Clojure =")
|
||||
|
||||
(assert (= 0 (ct-eval ctx "(count (hash-map))")) "count empty")
|
||||
(assert (= 2 (ct-eval ctx "(count (hash-map :a 1 :b 2))")) "count two")
|
||||
(assert (= 1 (ct-eval ctx "(get (hash-map :a 1 :b 2) :a)")) "get present")
|
||||
(assert (= nil (ct-eval ctx "(get (hash-map :a 1) :z)")) "get missing"))
|
||||
(print " passed")
|
||||
|
||||
# ============================================================
|
||||
# 2. assoc and dissoc
|
||||
# ============================================================
|
||||
(print "2: assoc/dissoc...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx "(= (assoc (hash-map :a 1) :b 2) (hash-map :a 1 :b 2))")) "assoc add")
|
||||
(assert (= true (ct-eval ctx "(= (assoc (hash-map :a 1) :a 99) (hash-map :a 99))")) "assoc replace")
|
||||
(assert (= true (ct-eval ctx "(= (dissoc (hash-map :a 1 :b 2) :a) (hash-map :b 2))")) "dissoc")
|
||||
(assert (= true (ct-eval ctx "(contains? (hash-map :a 1) :a)")) "contains? true")
|
||||
(assert (= false (ct-eval ctx "(contains? (hash-map :a 1) :z)")) "contains? false"))
|
||||
(print " passed")
|
||||
|
||||
# ============================================================
|
||||
# 3. keys, vals, merge
|
||||
# ============================================================
|
||||
(print "3: keys/vals/merge...")
|
||||
(let [ctx (init)]
|
||||
(assert (= 2 (ct-eval ctx "(count (keys (hash-map :a 1 :b 2)))")) "keys count")
|
||||
(assert (= 2 (ct-eval ctx "(count (vals (hash-map :a 1 :b 2)))")) "vals count")
|
||||
(assert (= true (ct-eval ctx "(= (merge (hash-map :a 1) (hash-map :b 2)) (hash-map :a 1 :b 2))")) "merge"))
|
||||
|
||||
(print " passed")
|
||||
|
||||
# ============================================================
|
||||
# 4. Empty and seq
|
||||
# ============================================================
|
||||
(print "4: empty? and seq...")
|
||||
(let [ctx (init)]
|
||||
(assert (= true (ct-eval ctx "(empty? (hash-map))")) "empty? true")
|
||||
(assert (= false (ct-eval ctx "(empty? (hash-map :a 1))")) "empty? false")
|
||||
(assert (= 1 (ct-eval ctx "(count (seq (hash-map :a 1)))")) "seq count"))
|
||||
(print " passed")
|
||||
|
||||
# ============================================================
|
||||
# 5. Larger maps
|
||||
# ============================================================
|
||||
(print "5: larger maps...")
|
||||
(let [ctx (init)]
|
||||
(eval-string ctx "
|
||||
(def big-map
|
||||
(reduce (fn [m i] (assoc m (keyword (str \"k\" i)) i))
|
||||
(hash-map)
|
||||
(range 100)))")
|
||||
(assert (= 100 (ct-eval ctx "(count big-map)")) "count 100")
|
||||
(assert (= 42 (ct-eval ctx "(get big-map :k42)")) "get k42"))
|
||||
(print " passed")
|
||||
|
||||
(print "\nAll PersistentHashMap tests passed!")
|
||||
140
test/unit/reader-test.janet
Normal file
140
test/unit/reader-test.janet
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
(use ../../src/jolt/reader)
|
||||
|
||||
# Helper: create a symbol
|
||||
(defn sym [name]
|
||||
(let [slash (string/find "/" name)]
|
||||
(if slash
|
||||
{:jolt/type :symbol
|
||||
:ns (string/slice name 0 slash)
|
||||
:name (string/slice name (+ slash 1))}
|
||||
{:jolt/type :symbol
|
||||
:ns nil
|
||||
:name name})))
|
||||
|
||||
# Symbols
|
||||
(assert (deep= (sym "foo") (parse-string "foo"))
|
||||
"bare symbol")
|
||||
(assert (deep= (sym "foo/bar") (parse-string "foo/bar"))
|
||||
"namespaced symbol")
|
||||
(assert (deep= (sym "+") (parse-string "+"))
|
||||
"operator symbol")
|
||||
(assert (deep= (sym "->foo") (parse-string "->foo"))
|
||||
"arrow symbol")
|
||||
|
||||
# Keywords
|
||||
(assert (= :foo (parse-string ":foo"))
|
||||
"bare keyword")
|
||||
(assert (= :foo/bar (parse-string "::foo/bar"))
|
||||
"auto-resolved keyword")
|
||||
(assert (= :foo/bar (parse-string ":foo/bar"))
|
||||
"namespaced keyword")
|
||||
|
||||
# Numbers
|
||||
(assert (= 1 (parse-string "1"))
|
||||
"integer")
|
||||
(assert (= -42 (parse-string "-42"))
|
||||
"negative integer")
|
||||
(assert (= 3.14 (parse-string "3.14"))
|
||||
"float")
|
||||
|
||||
# Strings
|
||||
(assert (= "hello" (parse-string "\"hello\""))
|
||||
"simple string")
|
||||
|
||||
# Nil, booleans
|
||||
(assert (= nil (parse-string "nil"))
|
||||
"nil")
|
||||
(assert (= true (parse-string "true"))
|
||||
"true")
|
||||
(assert (= false (parse-string "false"))
|
||||
"false")
|
||||
|
||||
# Lists → Janet arrays (to distinguish from vectors)
|
||||
(assert (array? (parse-string "(1 2 3)"))
|
||||
"list produces array")
|
||||
(assert (deep= @[1 2 3] (parse-string "(1 2 3)"))
|
||||
"simple list")
|
||||
|
||||
# Vectors → Janet tuples
|
||||
(assert (tuple? (parse-string "[1 2 3]"))
|
||||
"vector produces tuple")
|
||||
(assert (deep= [1 2 3] (parse-string "[1 2 3]"))
|
||||
"simple vector")
|
||||
|
||||
# Maps → Janet structs
|
||||
(let [m (parse-string "{:a 1 :b 2}")]
|
||||
(assert (struct? m) "map is struct")
|
||||
(assert (= 1 (m :a)) "map key lookup"))
|
||||
|
||||
# Sets → tagged with :jolt/set
|
||||
(let [form (parse-string "#{1 2 3}")]
|
||||
(assert (struct? form) "set is struct")
|
||||
(assert (= :jolt/set (form :jolt/type)) "set type tag"))
|
||||
|
||||
# Quote and shorthand
|
||||
(assert (deep= @[(sym "quote") (sym "x")] (parse-string "'x"))
|
||||
"quote shorthand")
|
||||
(assert (deep= @[(sym "syntax-quote") (sym "x")] (parse-string "`x"))
|
||||
"syntax-quote")
|
||||
(assert (deep= @[(sym "unquote") (sym "x")] (parse-string "~x"))
|
||||
"unquote")
|
||||
(assert (deep= @[(sym "unquote-splicing") (sym "x")] (parse-string "~@x"))
|
||||
"unquote-splicing")
|
||||
(assert (deep= @[(sym "deref") (sym "x")] (parse-string "@x"))
|
||||
"deref shorthand")
|
||||
|
||||
# Metadata
|
||||
(let [form (parse-string "^:meta x")]
|
||||
(assert (array? form) "metadata is array")
|
||||
(assert (deep= @[(sym "with-meta") (sym "x") :meta] form)
|
||||
"metadata form"))
|
||||
|
||||
# Comments (skip to end of line)
|
||||
(assert (= 42 (parse-string "; comment\n42"))
|
||||
"comment then form")
|
||||
|
||||
# Discard #_
|
||||
(assert (= 42 (parse-string "#_ (ignored 1 2) 42"))
|
||||
"discard skips next form")
|
||||
|
||||
# Anonymous function #()
|
||||
(let [form (parse-string "#(+ %1 %2)")]
|
||||
(assert (array? form) "fn form is array")
|
||||
(assert (deep= (sym "fn*") (in form 0)) "first element is fn*"))
|
||||
|
||||
# Nested forms
|
||||
(let [form (parse-string "(+ 1 (* 2 3))")]
|
||||
(assert (array? form) "outer list is array")
|
||||
(assert (deep= (sym "+") (in form 0)) "+ is first")
|
||||
(assert (= 1 (in form 1)) "1 is second")
|
||||
(assert (array? (in form 2)) "nested list is array"))
|
||||
|
||||
# Multiple forms: parse-next
|
||||
(let [[form1 rest-str] (parse-next "(1 2) [3 4]")]
|
||||
(assert (deep= @[1 2] form1) "first form is list")
|
||||
(let [[form2 _] (parse-next rest-str)]
|
||||
(assert (deep= [3 4] form2) "second form is vector")))
|
||||
|
||||
# Reader conditional — resolves :clj branch at read time
|
||||
(assert (= 1 (parse-string "#?(:clj 1 :cljs 2)"))
|
||||
"#?(:clj) picks :clj branch")
|
||||
(assert (= nil (parse-string "#?(:cljs 999)"))
|
||||
"#?(:cljs) returns nil on CLJ")
|
||||
(assert (= 42 (parse-string "#?(:clj 42)"))
|
||||
"#?(:clj) with single branch")
|
||||
(assert (deep= (sym "clj-only") (parse-string "#?(:clj clj-only :cljs cljs-only)"))
|
||||
"#?(:clj) picks :clj symbol")
|
||||
# Nested inside a list — :clj branch is evaluated at read time
|
||||
(assert (deep= @[(sym "+") 1 3] (parse-string "(+ 1 #?(:clj 3 :cljs 4))"))
|
||||
"#? inside list picks :clj")
|
||||
|
||||
# Characters — the reader now produces char values {:jolt/type :jolt/char :ch N}
|
||||
(let [form (parse-string "\\newline")]
|
||||
(assert (struct? form) "char is struct")
|
||||
(assert (= :jolt/char (form :jolt/type)) "char type")
|
||||
(assert (= 10 (form :ch)) "newline codepoint"))
|
||||
|
||||
(let [form (parse-string "\\a")]
|
||||
(assert (= 97 (form :ch)) "simple char codepoint"))
|
||||
|
||||
(print "All reader tests passed!")
|
||||
118
test/unit/types-test.janet
Normal file
118
test/unit/types-test.janet
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
(use ../../src/jolt/types)
|
||||
|
||||
# ============================================================
|
||||
# Var tests
|
||||
# ============================================================
|
||||
|
||||
# make-var
|
||||
(let [v (make-var 'x 42)]
|
||||
(assert (var? v) "var? returns true")
|
||||
(assert (= 42 (var-get v)) "var-get returns root binding")
|
||||
(assert (deep= {:name 'x} (var-meta v)) "var-meta returns metadata")
|
||||
(assert (deep= 'x (var-name v)) "var-name returns name symbol"))
|
||||
|
||||
# var without init value
|
||||
(let [v (make-var 'y)]
|
||||
(assert (var? v) "unbound var is still a var"))
|
||||
|
||||
# dynamic var
|
||||
(let [v (make-var '*dyn* 1 {:dynamic true})]
|
||||
(assert (var-dynamic? v) "var-dynamic? true")
|
||||
(assert (not (var-macro? v)) "var-macro? false for dynamic var"))
|
||||
|
||||
# macro var
|
||||
(let [v (make-var 'when nil {:macro true})]
|
||||
(assert (var-macro? v) "var-macro? true"))
|
||||
|
||||
# var-set — set root binding
|
||||
(let [v (make-var 'x 1)]
|
||||
(var-set v 99)
|
||||
(assert (= 99 (var-get v)) "var-set changes root binding"))
|
||||
|
||||
# alter-var-root
|
||||
(let [v (make-var 'c 0)]
|
||||
(alter-var-root v inc)
|
||||
(assert (= 1 (var-get v)) "alter-var-root applies fn"))
|
||||
|
||||
# with-meta — returns new var with updated meta
|
||||
(let [v (make-var 'x 42)
|
||||
v2 (with-meta v {:private true})]
|
||||
(assert (deep= {:name 'x :private true} (var-meta v2)) "with-meta merges meta")
|
||||
(assert (= 42 (var-get v2)) "with-meta preserves root binding"))
|
||||
|
||||
# var with namespace
|
||||
(let [ns (make-ns 'my.ns)
|
||||
v (make-var 'my.ns/x 1 {:ns ns})]
|
||||
(assert (= ns (var-ns v)) "var-ns returns namespace"))
|
||||
|
||||
# ============================================================
|
||||
# Namespace tests
|
||||
# ============================================================
|
||||
|
||||
(let [ns (make-ns 'foo.bar)]
|
||||
(assert (ns? ns) "ns? returns true")
|
||||
(assert (deep= 'foo.bar (ns-name ns)) "ns-name returns name symbol")
|
||||
(assert (table? (ns-map ns)) "ns-map returns table")
|
||||
(assert (= 0 (length (ns-map ns))) "empty namespace has no mappings"))
|
||||
|
||||
# ns-intern
|
||||
(let [ns (make-ns 'test.ns)
|
||||
v (ns-intern ns 'x 42)]
|
||||
(assert (var? v) "ns-intern returns a var")
|
||||
(assert (= 42 (var-get v)) "ns-intern sets root binding")
|
||||
# check ns-find returns the same var (by reference, not deep=)
|
||||
(assert (= v (ns-find ns 'x)) "ns-find returns interned var"))
|
||||
|
||||
# ns-intern without value
|
||||
(let [ns (make-ns 'test.ns)
|
||||
v (ns-intern ns 'y)]
|
||||
(assert (var? v) "ns-intern without value creates unbound var"))
|
||||
|
||||
# ns-unmap
|
||||
(let [ns (make-ns 'test.ns)
|
||||
_ (ns-intern ns 'x 1)
|
||||
_ (ns-unmap ns 'x)]
|
||||
(assert (nil? (ns-find ns 'x)) "ns-unmap removes mapping"))
|
||||
|
||||
# ns-resolve — own ns
|
||||
(let [ns (make-ns 'test.ns)
|
||||
v (ns-intern ns 'x 10)]
|
||||
(assert (= v (ns-resolve ns 'x)) "ns-resolve finds var in own ns"))
|
||||
|
||||
# ns-import
|
||||
(let [ns (make-ns 'test.ns)]
|
||||
(ns-import ns 'Date 'java.util.Date)
|
||||
(assert (= 'java.util.Date (ns-import-lookup ns 'Date)) "ns-import-lookup returns import"))
|
||||
|
||||
# ============================================================
|
||||
# Context tests
|
||||
# ============================================================
|
||||
|
||||
(let [ctx (make-ctx)]
|
||||
(assert (ctx? ctx) "ctx? returns true"))
|
||||
|
||||
# ctx with initial namespaces
|
||||
(let [ctx (make-ctx {:namespaces {"user" {"x" 1 "y" 2}}})]
|
||||
(let [ns (ctx-find-ns ctx "user")]
|
||||
(assert (ns? ns) "ctx-find-ns returns namespace for user")
|
||||
(let [v (ns-find ns "x")]
|
||||
(assert (var? v) "user/x is a var")
|
||||
(assert (= 1 (var-get v)) "user/x has correct value"))))
|
||||
|
||||
# ctx-find-ns creates ns if not present
|
||||
(let [ctx (make-ctx)
|
||||
ns (ctx-find-ns ctx "foo")]
|
||||
(assert (ns? ns) "ctx-find-ns creates namespace on demand"))
|
||||
|
||||
# ============================================================
|
||||
# Dynamic binding support (thread-local bindings table)
|
||||
# ============================================================
|
||||
|
||||
# push-thread-bindings / pop-thread-bindings
|
||||
(let [v (make-var '*dyn* 0 {:dynamic true})]
|
||||
(push-thread-bindings @{v 100})
|
||||
(assert (= 100 (var-get v)) "push-thread-bindings sets binding")
|
||||
(pop-thread-bindings)
|
||||
(assert (= 0 (var-get v)) "pop-thread-bindings restores root"))
|
||||
|
||||
(print "All types tests passed!")
|
||||
Loading…
Add table
Add a link
Reference in a new issue