# Specification: keyword-invoke and map-literal semantics that the compiled
# fast paths (jolt-4vr: inlined keyword lookup, inlined struct construction)
# must preserve. Written BEFORE the optimization; every row passes on the
# generic jolt-call/build-map-literal paths and must keep passing on the
# inlined ones.
(use ../support/harness)

(defspec "maps / keyword invoke"
  ["hit"                 "1"     "(:a {:a 1 :b 2})"]
  ["miss"                "nil"   "(:z {:a 1})"]
  ["miss with default"   ":d"    "(:z {:a 1} :d)"]
  ["hit with default"    "1"     "(:a {:a 1} :d)"]
  ["on nil"              "nil"   "(:a nil)"]
  ["on nil with default" ":d"    "(:a nil :d)"]
  ["nil value is present" "nil"  "(:a {:a nil} :d)"]
  ["false value is present" "false" "(:a {:a false} :d)"]
  ["on a vector"         "nil"   "(:a [1 2 3])"]
  ["on a number"         "nil"   "(:a 42)"]
  ["on a sorted map"     "2"     "(:b (sorted-map :a 1 :b 2))"]
  ["on assoc result"     "3"     "(:c (assoc {:a 1} :c 3))"]
  ["on a record field"   "5"     "(do (defrecord KFP [x]) (:x (->KFP 5)))"]
  ["qualified keyword"   "1"     "(:n/a {:n/a 1})"]
  ["nested in expr"      "6"     "(+ (:a {:a 1}) (:b {:b 2}) (:c {:c 3}))"]
  ["evaluates map expr once" "[2 1]"
   "(do (def cnt (atom 0)) (let [v (:a (do (swap! cnt inc) {:a 2}))] [v @cnt]))"])

(defspec "maps / literal construction"
  ["basic"            "{:a 1, :b 2}"  "{:a 1 :b 2}"]
  ["empty"            "{}"            "{}"]
  ["computed values"  "{:a 3}"        "{:a (+ 1 2)}"]
  ["nil value kept"   "true"          "(contains? {:a nil} :a)"]
  ["nil value lookup" "nil"           "(get {:a nil} :a :d)"]
  ["string key"       "1"             "(get {\"k\" 1} \"k\")"]
  ["number key"       ":one"          "(get {1 :one} 1)"]
  ["collection key"   ":v"            "(get {[1 2] :v} [1 2])"]
  ["collection value-equal key" ":v"  "(get {[1 2] :v} (vector 1 2))"]
  ["computed key"     "1"             "(get {(keyword \"a\") 1} :a)"]
  # jolt-p3c: literal entries evaluate left to right in SOURCE order (the
  # reader carries [k v ...] order on a struct prototype / phm field)
  ["values evaluate in source order" "[1 2 3]"
   "(do (def log (atom [])) {:a (swap! log conj 1) :b (swap! log conj 2) :c (swap! log conj 3)} (deref log))"]
  ["keys evaluate before their values, pairwise" "[:k1 :v1 :k2 :v2]"
   "(do (def log (atom [])) {(do (swap! log conj :k1) :a) (do (swap! log conj :v1) 1) (do (swap! log conj :k2) :b) (do (swap! log conj :v2) 2)} (deref log))"]
  ["source order with a nil value (phm form)" "[1 2 3]"
   "(do (def log (atom [])) {:a (do (swap! log conj 1) nil) :b (swap! log conj 2) :c (swap! log conj 3)} (deref log))"]
  ["source order through syntax-quote" "[1 2]"
   "(do (def log (atom [])) (defmacro m-p3c [] `{:a ~(list 'swap! 'log 'conj 1) :b ~(list 'swap! 'log 'conj 2)}) (m-p3c) (deref log))"]
  ["count"            "3"             "(count {:a 1 :b 2 :c 3})"]
  ["equality with phm" "true"         "(= {:a 1 :b 2} (assoc {:a 1} :b 2))"]
  ["keys work after assoc" "2"        "(:b (assoc {:a 1 :b 2} :c 3))"]
  ["literal in fn body"  "12"         "(do (defn mfp-mk [x] {:v (* x 2)}) (:v (mfp-mk 6)))"])

(defspec "clojure.math (jolt-h79)"
  ["sqrt"        "true"  "(< 1.4142 (clojure.math/sqrt 2) 1.4143)"]
  ["pow"         "1024"  "(long (clojure.math/pow 2 10))"]
  ["tan of 0"    "0"     "(long (clojure.math/tan 0))"]
  ["round"       "3"                "(clojure.math/round 2.6)"]
  ["floor"       "2"                "(clojure.math/floor 2.9)"]
  ["signum"      "-1"               "(clojure.math/signum -7.2)"]
  ["to-radians"  "true"             "(< 3.14 (clojure.math/to-radians 180) 3.15)"]
  ["PI"          "true"             "(< 3.14 clojure.math/PI 3.15)"]
  ["require + alias" "5"            "(do (require '[clojure.math :as m]) (long (m/hypot 3 4)))"]
  ["as a value"  "[1 2]"            "(mapv (comp long clojure.math/sqrt) [1 4])"])

# jolt-507: a jolt local in janet CALL-HEAD position must not resolve as a
# janet core macro of the same name (repeat/seq/loop are janet macros). The
# emitter rebinds local callees to reserved _fp$ symbols.
(defspec "calls / locals named like janet macros"
  ["local fn named repeat"  "[1 1]"    "(let [repeat (fn [x] [x x])] (repeat 1))"]
  ["local fn named seq"     ":end"     "((fn seq [n] (if (pos? n) (seq (dec n)) :end)) 2)"]
  ["local fn named loop2"   "[2 1]"    "(let [with (fn [a b] [b a])] (with 1 2))"]
  ["overlay repeat self-call (regression)" "(quote (0 0 0))" "(take 3 (repeat 0))"]
  ["closure param called"   "42"       "((fn [f] (f 41)) inc)"]
  ["param holding a keyword (IFn leftover)" "1" "((fn [f] (f {:a 1})) :a)"])
