jolt-p3c: Clojure evaluates map-literal entries left to right, but the
reader represented map forms as bare janet structs, so entries ran in
hash order. The reader now carries [k v ...] source order out-of-band —
on a struct PROTOTYPE (keys/kvs/length ignore protos, so macros that
get/keys literal map forms see no change; jolt-equal? was already
structural) and as a plain field on the phm rep (nil key/value). The
analyzer (form-map-pairs), the interpreter's map eval, both
syntax-quote walks, and core-sqmap (the lowered `{...} builder — the
array-map case, where Clojure also preserves insertion order) all honor
it, so the order survives macroexpansion in both modes.
jolt-507 root-caused: the parked inline put a LOCAL in janet call-head
position for the first time, and janet resolves head symbols against
the macro table before lexical upvalues — clojure.core/repeat's
self-name local expanded as janet's (repeat n & body) macro, compiling
the self-call into a countdown loop returning nil. Everything in the
issue (interpose, interleave) traced to that one name collision. The
emitter now rebinds local callees to reserved _fp$ symbols (argument
positions never consult the macro table), and the inline — direct
calls for function locals, jolt-call only for IFn-collection
leftovers — lands. Spec rows pin locals named repeat/seq/with called
in head position.
Gate green, suite 4718 steady, bench even with main.
74 lines
4.5 KiB
Text
74 lines
4.5 KiB
Text
# 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)"])
|