core: staged recompile for early defns; keys/vals/empty? leave the seed (jolt-4j3)
recompile-defns! is the defn analog of recompile-macros!: pre/at-kernel
overlay defns (00-syntax's destructure and friends; the kernel tier too in
interpret mode) load as interpreted closures, the evaluator stashes their fn
source on the var (:defn-src, scoped by a flag only api/load-core-overlay!
sets), and the end-of-init pass compiles them and swaps the var root. With
that in place, keys/vals/empty? — the fns the 00-syntax expanders call at
expansion time — move to the top of 00-syntax as raw fn* defs (canonical:
keys/vals project (seq m), so sorted maps come back in comparator order and
(keys {}) is nil; empty? keeps O(1) count dispatch with seq's cell check only
for the lazy/list fallback). The sorted tier drops its now-dead :keys/:vals
ops.
Correctness fixes that surfaced once the gate was run with a REAL exit code
(the previous 'jpm test | grep' gates reported grep's exit and masked spec
failures across #48-#50):
- map conj is strict again: a non-nil/non-map arg must be a 2-element vector
('Vector arg to map conj must be a pair'), and merge inherits it — the
batch-2 canonical merge had silently dropped the validation
- conj onto a lazy seq prepends (it fell into the MAP fallback); upstream
clojure.data/diff relies on (conj seq x) via set/union over keys, so diff
now matches Clojure exactly
- (seq {}) / (seq #{}) / empty phm are nil, not ()
- key/val are strict (a plain vector is not an entry); find mints a REAL
entry as the first entry of a one-entry map, nil values intact
- the sci avoid-method-too-large stub passes its registry map through
instead of returning a raw host table (strict conj rejected it; sci's
clojure-core registry is also no longer discarded)
Test updates: lazy-infinite pins take-nth realization at 5 (was 7 — the
canonical lazy impl realizes fewer); self-host asserts the analyzer IS loaded
in interpret mode (compiled expanders, PR #50) and is NOT in the
:compile-macros? false oracle. 18 new maps-spec rows.
Gate: jpm test exit 0 (verified directly, not through a pipe), conformance
326x3, suite 4698 >= 4660.
This commit is contained in:
parent
e6f562c175
commit
63eb6eca6e
11 changed files with 176 additions and 50 deletions
|
|
@ -75,7 +75,9 @@
|
|||
["LAZY take-while" "6" "(do (def c (atom 0)) (dorun (take-while (fn [x] (swap! c inc) (< x 5)) (range))) @c)"]
|
||||
["LAZY drop-while" "6" "(do (def c (atom 0)) (dorun (take 3 (drop-while (fn [x] (swap! c inc) (< x 5)) (range)))) @c)"]
|
||||
["LAZY distinct" "4" "(do (def c (atom 0)) (dorun (take 3 (distinct (map (fn [x] (swap! c inc) x) (cycle [1 2 1 3 1]))))) @c)"]
|
||||
["LAZY take-nth" "7" "(do (def c (atom 0)) (dorun (take 3 (take-nth 2 (map (fn [x] (swap! c inc) x) (range))))) @c)"]
|
||||
# 5, was 7: the canonical lazy take-nth (40-lazy, jolt-ded batch 3) realizes
|
||||
# only the elements the taken outputs and their drops touch.
|
||||
["LAZY take-nth" "5" "(do (def c (atom 0)) (dorun (take 3 (take-nth 2 (map (fn [x] (swap! c inc) x) (range))))) @c)"]
|
||||
["LAZY map-indexed" "3" "(do (def c (atom 0)) (dorun (take 3 (map-indexed (fn [i x] (swap! c inc) [i x]) (range)))) @c)"]
|
||||
["LAZY keep" "6" "(do (def c (atom 0)) (dorun (take 3 (keep (fn [x] (swap! c inc) (if (odd? x) x nil)) (range)))) @c)"]
|
||||
["LAZY keep-indexed" "6" "(do (def c (atom 0)) (dorun (take 3 (keep-indexed (fn [i x] (swap! c inc) (if (odd? i) x)) (range)))) @c)"]
|
||||
|
|
|
|||
|
|
@ -75,3 +75,17 @@
|
|||
(os/setenv "JOLT_INTERPRET_MACROS" nil)
|
||||
|
||||
(print "compiled macro expansion passed!")
|
||||
|
||||
# 6. Early overlay DEFNS get the same staged-recompile treatment (jolt-4j3):
|
||||
# 00-syntax fns (destructure, and the expander-called keys/vals/empty?) load
|
||||
# interpreted pre-kernel, then compile in the same end-of-init pass.
|
||||
(each nm ["destructure" "empty?" "keys" "vals"]
|
||||
(def v (macro-var ictx nm))
|
||||
(assert v (string nm " var exists"))
|
||||
(assert (get v :defn-compiled)
|
||||
(string nm " early defn is compiled (interpret mode)")))
|
||||
(def cctx2 (init {:compile? true}))
|
||||
(assert (get (macro-var cctx2 "destructure") :defn-compiled)
|
||||
"early defn compiled in compile mode too")
|
||||
(assert (not (get (macro-var octx "destructure") :defn-compiled))
|
||||
"oracle mode keeps early defns interpreted")
|
||||
|
|
|
|||
|
|
@ -70,9 +70,17 @@
|
|||
# Proof the self-hosted pipeline actually ran: only backend/ensure-analyzer
|
||||
# populates jolt.analyzer. An interpret-only ctx never loads it.
|
||||
(assert (pos? (length ((ctx-find-ns ctx "jolt.analyzer") :mappings))) "analyzer loaded under :compile?"))
|
||||
# Interpret mode now loads the analyzer too — for compiled macro expansion
|
||||
# (ensure-macros-compiled!, every mode). The fully-interpreted oracle is the
|
||||
# :compile-macros? false ctx, which must never touch the analyzer.
|
||||
(let [ctx (init-cached {})]
|
||||
(eval-one ctx (parse-string "(+ 1 2)"))
|
||||
(assert (zero? (length ((ctx-find-ns ctx "jolt.analyzer") :mappings))) "analyzer NOT loaded when interpreting"))
|
||||
(assert (pos? (length ((ctx-find-ns ctx "jolt.analyzer") :mappings)))
|
||||
"analyzer loaded when interpreting (compiled expanders)"))
|
||||
(let [ctx (init-cached {:compile-macros? false})]
|
||||
(eval-one ctx (parse-string "(+ 1 2)"))
|
||||
(assert (zero? (length ((ctx-find-ns ctx "jolt.analyzer") :mappings)))
|
||||
"analyzer NOT loaded in the interpreted-macro oracle"))
|
||||
|
||||
# clojure.core overlay: fns moved from core.janet to jolt-core/clojure/core.clj
|
||||
# load into clojure.core at init and work the same compiled or interpreted.
|
||||
|
|
|
|||
|
|
@ -155,3 +155,25 @@
|
|||
["update-vals empty" "{}" "(update-vals {} inc)"]
|
||||
["update-vals nil" "{}" "(update-vals nil inc)"]
|
||||
["update-vals keeps keys" "[:a :b]" "(sort (keys (update-vals {:a 1 :b 2} inc)))"])
|
||||
|
||||
# keys/vals/empty? are 00-syntax overlay fns now (jolt-4j3) — expander-called,
|
||||
# so they live in the first tier and get the staged defn recompile.
|
||||
(defspec "maps / keys-vals-empty? as overlay fns"
|
||||
["keys" "(quote (:a))" "(keys {:a 1})"]
|
||||
["keys empty map" "nil" "(keys {})"]
|
||||
["keys nil" "nil" "(keys nil)"]
|
||||
["vals" "(quote (1))" "(vals {:a 1})"]
|
||||
["vals empty" "nil" "(vals {})"]
|
||||
["keys sorted order" "[1 2 3]" "(vec (keys (sorted-map 2 :b 1 :a 3 :c)))"]
|
||||
["vals sorted order" "[:a :b :c]" "(vec (vals (sorted-map 2 :b 1 :a 3 :c)))"]
|
||||
["keys/vals zip" "{:a 1 :b 2}" "(zipmap (keys {:a 1 :b 2}) (vals {:a 1 :b 2}))"]
|
||||
["empty? map" "true" "(empty? {})"]
|
||||
["empty? vec" "[true false]" "[(empty? []) (empty? [1])]"]
|
||||
["empty? list" "[true false]" "[(empty? ()) (empty? (list 1))]"]
|
||||
["empty? string" "[true false]" "[(empty? \"\") (empty? \"a\")]"]
|
||||
["empty? nil" "true" "(empty? nil)"]
|
||||
["empty? set" "[true false]" "[(empty? #{}) (empty? #{1})]"]
|
||||
["empty? lazy" "[true false]" "[(empty? (filter pos? [-1])) (empty? (map inc [1]))]"]
|
||||
["empty? lazy nil elem" "false" "(empty? (cons nil nil))"]
|
||||
["empty? sorted" "[true false]" "[(empty? (sorted-map)) (empty? (sorted-set 1))]"]
|
||||
["empty? number throws" :throws "(empty? 5)"])
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue