- types.janet: find-var (ctx-based var resolution), alter-meta!, reset-meta! - evaluator.janet: 10 new special-form dispatch arms for var ops - core.janet: 6 new Clojure-level wrappers in core-bindings - core-meta: add var-aware branch (was struct-only) - core-binding macro: use array-map instead of hash-map (PHM incompatibility) - 8 new tests (17: var system, 18: var metadata) — all pass - 317 total tests, 0 failures
287 lines
11 KiB
Text
287 lines
11 KiB
Text
# 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 "(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!")
|