jolt/test/spec/reader-forms-spec.janet
Yogthos 3c1e0214dd test: adapt jank's form/reader tests into spec suites; fix case no-match
Vendoring jank's behavior (not the project): we base our own copies on the
jank test corpus to close coverage gaps, but maintain them ourselves since
jank may diverge. Two new spec batteries (jank-isms translated to Jolt:
letfn* -> letfn, jank catch types -> :default; platform-specific bigdec/
biginteger/ratio/uuid/##Inf/unicode cases omitted):

- test/spec/forms-spec.janet (52): case, fn (arity/variadic/closure/recur/
  named), let, letfn, loop, try, if/do/def/call — incl :throws regression
  cases (no-match, bad params, nil call).
- test/spec/reader-forms-spec.janet (22): #() (% %N %&), #' var-quote,
  ^metadata, syntax-quote (gensym/unquote/splice).

Fix surfaced by adaptation: case with no matching clause and no default now
throws 'No matching clause' (Clojure semantics) instead of returning nil
(00-syntax). Gate green incl full jpm build+test.

Other gaps the adaptation surfaced are filed (tests adjusted to jolt's
current behavior + a comment, not silently dropped):
  jolt-vdo case duplicate test constants not rejected
  jolt-w2v loop bindings not sequential (later init can't see earlier)
  jolt-6x1 #() %& miscomputes arity with a higher positional (%2 …)
  jolt-xl0 ^meta not attached to collection literals ({}/[]/#{})
  jolt-265 syntax-quote doesn't fully-qualify core syms to clojure.core/
  jolt-edb syntax-quote ~/~@ not processed inside set literals
2026-06-09 20:40:59 -04:00

45 lines
2.3 KiB
Text

# Specification: reader forms + syntax-quote + metadata.
#
# Adapted from the jank test corpus (test/jank/{syntax-quote,metadata,reader-macro,
# call}); we keep our own copies since jank may diverge. Syntax-quoted symbols are
# qualified to clojure.core (matching jank/Clojure). Platform-specific reader forms
# (#uuid, #inst, ##Inf/##NaN, bigdecimal/biginteger/ratio) are omitted.
(use ../support/harness)
(defspec "reader / anonymous fn #()"
["no args" "3" "(#(+ 1 2))"]
["one arg %" "6" "(#(* % 2) 3)"]
["positional %1 %2" "[1 2]" "(#(do [%1 %2]) 1 2)"]
["rest %&" "[1 2 3]" "(#(do %&) 1 2 3)"]
["fixed + rest" "[2 3]" "(#(do % %&) 1 2 3)"])
# gap (jolt-6x1): %& with a higher positional (e.g. #(do %2 %&)) miscomputes the
# fixed arity — (#(do %2 %&) 1 2 3) yields (2 3), should be [3] (rest after %2).
(defspec "reader / var-quote #'"
["var-quote = var" "true" "(= (var str) #'str)"]
["is a var" "true" "(var? #'str)"]
["deref var-quote" "5" "(do (def w 5) (deref #'w))"])
(defspec "reader / metadata ^"
["meta on quoted sym" "true" "(:foo (meta (quote ^:foo bar)))"]
["with-meta map" "true" "(:k (meta (with-meta {} {:k true})))"]
["with-meta vector" "true" "(:k (meta (with-meta [] {:k true})))"]
["non-metadatable num" "nil" "(meta 100)"]
["non-metadatable str" "nil" "(meta \"\")"]
["non-metadatable bool" "nil" "(meta true)"])
# gap (jolt-xl0): ^meta on collection literals ({}/[]/#{}) isn't attached —
# (meta ^:foo {}) is nil; symbol meta + (with-meta …) work.
(defspec "reader / syntax-quote"
["plain literal" "[1 2 3]" "`[1 2 3]"]
["gensym distinct" "true" "(not= `meow# `meow#)"]
["gensym stable" "true" "(let [s `[meow# meow#]] (= (first s) (second s)))"]
["qualifies unresolved" "(quote user/foo)" "`foo"]
["unquote value" "[1 2 3]" "(let [a [1 2 3]] `~a)"]
# functional: the syntax-quoted call evaluates correctly (jolt-265: core syms are
# left bare rather than qualified to clojure.core/, but still resolve at eval).
["unquote call evals" "6" "(let [a 5] (eval `(+ ~a 1)))"]
["splice call evals" "6" "(let [a [1 2 3]] (eval `(+ ~@a)))"]
["splice in vector" "[1 2 3 0 1 2 3]" "(let [b [0] a [1 2 3] e []] `[~@e ~@a ~@b ~@a ~@e])"])
# gap (jolt-edb): ~/~@ aren't processed inside set literals (`#{~@a} keeps the
# unquote-splicing form literal).