Chez Phase 3 inc 2: quote + collection literals in jolt.backend-scheme
Adds :vector/:map/:set (emit-ordered, left-to-right element eval) and :quote to
the portable Clojure emitter. Collection-literal nodes carry already-analyzed IR
items so they just recurse; quote walks the RAW reader form.
emit-quoted walks the reader form via the jolt.host form-* contract (form-list?/
form-elements/form-vec-items/form-map-pairs/form-set-items/form-sym-*), the same
portable seam the analyzer uses — not host-native predicates, so it works
unchanged whether the form came from the Janet reader or the Chez reader. Reader
forms are raw host representations (Janet list=array, vec=tuple), so native
list?/vector? would not see them; the contract is the correct abstraction and
keeps the emitter host-neutral. Quoted-symbol metadata and def-meta still defer
to inc 3.
Surfaced a latent Janet-host bug (jolt-tg9s): (quote #{...}) evaluates to the raw
reader form instead of a reconstructed set, so (contains? (quote #{:p :q}) :p) is
false on build/jolt. The Chez emitter is correct (real Clojure: true); the parity
test asserts the verified value for those two cases.
Gate: emit-parity 42/42 (incl vector/map/set literals, coll/kw-as-fn, quoted
list/vec/map/set/symbol/nested). emit-test 331/331, conformance 355x3. jolt-7jvp.
This commit is contained in:
parent
d6847b3ba4
commit
206fe94a95
2 changed files with 106 additions and 9 deletions
|
|
@ -12,11 +12,17 @@
|
|||
Janet emitter while the port is in flight.
|
||||
|
||||
INCREMENT 1 (jolt-hg7z): const/local/var/the-var/if/do/let/loop/recur/invoke
|
||||
(+ native-ops)/fn/def + the escaping/flonum/munge helpers — enough to compile
|
||||
fib/mandelbrot-shaped code end to end. Quote, collection literals, try/throw,
|
||||
host interop, regex/inst/uuid and program assembly land in later increments
|
||||
(they throw `:not-yet-ported` here so an accidental hit is loud)."
|
||||
(:require [clojure.string :as str]))
|
||||
(+ native-ops)/fn/def + the escaping/flonum/munge helpers.
|
||||
INCREMENT 2 (jolt-7jvp): collection literals (vector/map/set, emit-ordered) +
|
||||
quote (emit-quoted, walks the raw reader form via the portable jolt.host form-*
|
||||
contract — same seam the analyzer uses, so it stays host-neutral). Quoted symbol
|
||||
metadata, def-meta, try/throw, host interop, regex/inst/uuid and program
|
||||
assembly land in later increments (they throw not-yet-ported so a hit is loud)."
|
||||
(:require [clojure.string :as str]
|
||||
[jolt.host :refer [form-sym? form-sym-name form-sym-ns form-sym-meta
|
||||
form-list? form-vec? form-map? form-set? form-char?
|
||||
form-literal? form-elements form-vec-items
|
||||
form-map-pairs form-set-items]]))
|
||||
|
||||
;; Hot clojure.core primitives lowered to native Scheme, mirroring the Janet
|
||||
;; backend's native-ops. `=` is the exactness-aware jolt= from values.ss; inc/dec/
|
||||
|
|
@ -139,6 +145,46 @@
|
|||
(str "(integer->char " (:ch v) ")")
|
||||
:else (throw (ex-info (str "emit-const: unsupported literal " (pr-str v)) {}))))
|
||||
|
||||
;; Emit a call `(ctor a0 a1 ...)` with the args evaluated LEFT-TO-RIGHT. Chez's
|
||||
;; procedure-argument evaluation order is unspecified (in practice right-to-left),
|
||||
;; but Clojure evaluates collection-literal elements left to right, so a literal
|
||||
;; like [(read r) (read r)] over side-effecting reads must bind in source order.
|
||||
;; Bind each arg to a fresh temp in a let* then construct. Only wraps at >= 2 args.
|
||||
(defn- emit-ordered [ctor arg-strs]
|
||||
(if (< (count arg-strs) 2)
|
||||
(str "(" ctor (if (empty? arg-strs) "" (str " " (str/join " " arg-strs))) ")")
|
||||
(let [tmps (map (fn [_] (fresh-label "_o$")) arg-strs)
|
||||
binds (str/join " " (map (fn [t a] (str "(" t " " a ")")) tmps arg-strs))]
|
||||
(str "(let* (" binds ") (" ctor " " (str/join " " tmps) "))"))))
|
||||
|
||||
;; Quoted literals (jolt-u8j7). A :quote node's :form is the RAW reader form;
|
||||
;; reconstruct each as the matching Chez RT constructor — the runtime value of a
|
||||
;; quote is just that literal data. The form is walked via the jolt.host form-*
|
||||
;; contract (the portable seam the analyzer uses), NOT host-native predicates, so
|
||||
;; this stays host-neutral: on Janet the contract walks Janet reader forms, on
|
||||
;; Chez it walks Chez reader forms.
|
||||
(declare emit-quoted)
|
||||
(defn- emit-quoted-map [pairs]
|
||||
;; pairs: a jolt vector of [k-form v-form] pairs (form-map-pairs)
|
||||
(str "(jolt-hash-map "
|
||||
(str/join " " (mapcat (fn [p] [(emit-quoted (nth p 0)) (emit-quoted (nth p 1))]) pairs))
|
||||
")"))
|
||||
(defn- emit-quoted [form]
|
||||
(cond
|
||||
(form-char? form) (emit-const form)
|
||||
(form-literal? form) (emit-const form)
|
||||
(form-sym? form)
|
||||
(let [m (form-sym-meta form)]
|
||||
(when (and m (pos? (count m)))
|
||||
(throw (ex-info "emit-quoted: quoted symbol with metadata not yet ported (inc 3)" {})))
|
||||
(let [sns (form-sym-ns form) nm (form-sym-name form)]
|
||||
(str "(jolt-symbol " (if sns (chez-str-lit sns) "#f") " " (chez-str-lit nm) ")")))
|
||||
(form-set? form) (str "(jolt-hash-set " (str/join " " (map emit-quoted (form-set-items form))) ")")
|
||||
(form-list? form) (str "(jolt-list " (str/join " " (map emit-quoted (form-elements form))) ")")
|
||||
(form-vec? form) (str "(jolt-vector " (str/join " " (map emit-quoted (form-vec-items form))) ")")
|
||||
(form-map? form) (emit-quoted-map (form-map-pairs form))
|
||||
:else (throw (ex-info (str "emit-quoted: unsupported quoted form " (pr-str form)) {}))))
|
||||
|
||||
;; A def's :meta is a jolt map value. Non-empty? (a plain def carries {}).
|
||||
(defn- jmeta-nonempty? [m] (and (map? m) (pos? (count m))))
|
||||
|
||||
|
|
@ -305,6 +351,13 @@
|
|||
:do (str "(begin " (str/join " " (map emit (:statements node)))
|
||||
(if (empty? (:statements node)) "" " ") (emit (:ret node)) ")")
|
||||
:invoke (emit-invoke node)
|
||||
;; collection literals -> rt constructors (collections.ss). Elements are
|
||||
;; already-analyzed IR nodes; evaluate LEFT-TO-RIGHT (emit-ordered).
|
||||
:vector (emit-ordered "jolt-vector" (map emit (:items node)))
|
||||
:set (emit-ordered "jolt-hash-set" (map emit (:items node)))
|
||||
:map (emit-ordered "jolt-hash-map"
|
||||
(mapcat (fn [p] [(emit (nth p 0)) (emit (nth p 1))]) (:pairs node)))
|
||||
:quote (emit-quoted (:form node))
|
||||
:let (emit-let node)
|
||||
:loop (emit-loop node)
|
||||
:recur (emit-recur node)
|
||||
|
|
@ -315,7 +368,7 @@
|
|||
(:no-init node)
|
||||
(str "(declare-var! " (chez-str-lit (:ns node)) " " (chez-str-lit (:name node)) ")")
|
||||
(jmeta-nonempty? (:meta node))
|
||||
(throw (ex-info "emit: def with non-empty meta not yet ported (inc 2)" {}))
|
||||
(throw (ex-info "emit: def with non-empty meta not yet ported (inc 3)" {}))
|
||||
:else
|
||||
(str "(def-var! " (chez-str-lit (:ns node)) " " (chez-str-lit (:name node)) " "
|
||||
(emit (:init node)) ")"))
|
||||
|
|
|
|||
|
|
@ -88,11 +88,26 @@
|
|||
[code (string/trim out) (string/trim err)])
|
||||
|
||||
# A case passes when the Clojure emitter's Chez output equals the Janet oracle.
|
||||
# An emit-time throw (out-of-subset op) is a clean FAIL, not a crash.
|
||||
(defn check [name src]
|
||||
(def want (cli-oracle src))
|
||||
(def [code out err] (run-clj src))
|
||||
(ok name (and (= code 0) (= out want))
|
||||
(string "chez=" out " oracle=" want " code=" code " | " err)))
|
||||
(def res (protect (run-clj src)))
|
||||
(if (not (res 0))
|
||||
(ok name false (string "emit/run threw: " (res 1)))
|
||||
(let [[code out err] (res 1)]
|
||||
(ok name (and (= code 0) (= out want))
|
||||
(string "chez=" out " oracle=" want " code=" code " | " err)))))
|
||||
|
||||
# For cases where the Janet oracle carries a known latent bug (e.g. quoted-set
|
||||
# literals don't reconstruct, jolt-tg9s) but Chez is correct: assert Chez output
|
||||
# against the hand-verified real-Clojure value directly.
|
||||
(defn check-exact [name src want]
|
||||
(def res (protect (run-clj src)))
|
||||
(if (not (res 0))
|
||||
(ok name false (string "emit/run threw: " (res 1)))
|
||||
(let [[code out err] (res 1)]
|
||||
(ok name (and (= code 0) (= out want))
|
||||
(string "chez=" out " want=" want " code=" code " | " err)))))
|
||||
|
||||
# --- inc 1 subset: const/local/var/if/do/let/loop/recur/invoke/fn/def ----------
|
||||
|
||||
|
|
@ -113,6 +128,35 @@
|
|||
(check "truthy local" "(defn t [x] (if x 1 2)) (t false)")
|
||||
(check "mod rem quot" "(+ (mod 17 5) (rem 17 5) (quot 17 5))")
|
||||
(check "min max" "(+ (min 3 1 2) (max 3 1 2))")
|
||||
# --- inc 2 subset: collection literals (vector/map/set) + quote -----------------
|
||||
|
||||
(check "vector literal" "[1 2 3]")
|
||||
(check "nested vector" "[1 [2 3] [4 [5 6]]]")
|
||||
(check "vector of exprs" "[(+ 1 2) (* 3 4)]")
|
||||
(check "map literal eq" "(= {:a 1 :b 2} {:a 1 :b 2})")
|
||||
(check "map get" "(get {:a 1 :b 2} :b)")
|
||||
(check "set literal eq" "(= #{1 2 3} #{3 2 1})")
|
||||
(check "set contains" "(contains? #{1 2 3} 2)")
|
||||
(check "vector count" "(count [10 20 30 40])")
|
||||
(check "conj vector" "(conj [1 2] 3 4)")
|
||||
(check "assoc map" "(get (assoc {:a 1} :b 2) :b)")
|
||||
(check "coll as fn" "({:a 1 :b 2} :a)")
|
||||
(check "kw as fn" "(:b {:a 1 :b 2})")
|
||||
(check "kw as fn default" "(:z {:a 1} 99)")
|
||||
(check "quote list" "(count (quote (a b c d)))")
|
||||
(check "quote vector eq" "(= (quote [1 2 3]) [1 2 3])")
|
||||
(check "quote symbol eq" "(= (quote foo) (quote foo))")
|
||||
(check "quote ns symbol eq" "(= (quote my.ns/bar) (quote my.ns/bar))")
|
||||
(check "quote nested count" "(count (quote (a [1 2] {:k v})))")
|
||||
(check "quote keyword in list" "(first (quote (:a :b :c)))")
|
||||
(check "quote map" "(get (quote {:x 1 :y 2}) :y)")
|
||||
# Janet oracle is buggy here (jolt-tg9s): quoted-set doesn't reconstruct. Chez is
|
||||
# correct (real Clojure: true) — assert against the verified value.
|
||||
(check-exact "quote set contains" "(contains? (quote #{:p :q}) :p)" "true")
|
||||
(check-exact "quote set eq literal" "(= (quote #{1 2 3}) #{1 2 3})" "true")
|
||||
(check "vector map filter" "(into [] (filter even? (map inc [1 2 3 4 5])))")
|
||||
(check "map over literal" "(reduce + (vals {:a 1 :b 2 :c 3}))")
|
||||
|
||||
(check "mandelbrot run(20)"
|
||||
(string ``
|
||||
(defn count-point [cr ci cap]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue