Chez Phase 2 (inc U): list? + map-entry-as-vector + clojure.walk (jolt-75sv)
list? was nil on Chez because one cseq record backs both lists and lazy/
realized seqs. Add a list? marker field (cseq v2) set only on the HEAD cell
of a list -- (list ...), quoted list literals, cons, reverse, conj onto a
list. rest/next/seq/map therefore yield unmarked seq cells, so they are
seqs and not list?, matching the seed (where rest-of-a-list is a non-list
seq). Empty () is treated as a list.
vector?: drop the map-entry exclusion. Clojure's MapEntry implements
IPersistentVector and the seed agrees -- (vector? (first {:a 1})) is true.
Only dot-forms' coll dispatch read jolt-vector?, where a 2-vector entry is
correct.
clojure.walk + clojure.template join the prelude stdlib tier. The driver
now evals each stdlib ns's requires -- and the ns form's (:require ...)
clause -- so an aliased ref (template's walk/postwalk-replace) resolves at
emit time instead of lowering to an Unknown class host-static. ns forms are
evaled for that side effect but not emitted, so the runtime *ns* doesn't
leak to the last stdlib ns.
Parity 2163 -> 2176, 0 new divergences. New test/chez/_walk.janet 39/39.
This commit is contained in:
parent
3319684a38
commit
77026fa9ec
7 changed files with 162 additions and 33 deletions
|
|
@ -203,9 +203,11 @@
|
|||
(define (jolt-conj1 coll x)
|
||||
(cond ((pvec? coll) (pvec-conj coll x)) ; nil is a valid vector/set element
|
||||
((pset? coll) (pset-conj coll x))
|
||||
;; a list/seq conjs by PREPENDING (seq.ss: cseq / empty-list)
|
||||
((cseq? coll) (cseq-realized x coll))
|
||||
((empty-list-t? coll) (cseq-realized x jolt-nil))
|
||||
;; a list/seq conjs by PREPENDING (seq.ss: cseq / empty-list). conj onto a
|
||||
;; list stays a list, conj onto a lazy/realized seq yields a seq cell (a
|
||||
;; Cons) — list?-preserving, matching the seed.
|
||||
((cseq? coll) (if (cseq-list? coll) (cseq-list x coll) (cseq-realized x coll)))
|
||||
((empty-list-t? coll) (cseq-list x jolt-nil))
|
||||
((pmap? coll)
|
||||
(cond ((jolt-nil? x) coll) ; (conj m nil) = m
|
||||
((pmap? x) (pmap-fold x (lambda (k v m) (pmap-assoc m k v)) coll)) ; merge
|
||||
|
|
|
|||
|
|
@ -107,7 +107,11 @@
|
|||
# same analyze->emit pipeline lowers it; an aliased ref resolves via var-deref at
|
||||
# runtime once the alias is registered (the driver pre-evals requires). jolt-nfca.
|
||||
(def stdlib-ns-files
|
||||
[["clojure.string" "src/jolt/clojure/string.clj"]])
|
||||
[["clojure.string" "src/jolt/clojure/string.clj"]
|
||||
["clojure.walk" "src/jolt/clojure/walk.clj"]
|
||||
# clojure.template requires clojure.walk (apply-template over postwalk-replace)
|
||||
# — must follow it so the alias resolves at emit time.
|
||||
["clojure.template" "src/jolt/clojure/template.clj"]])
|
||||
|
||||
(defn- sym-name [x]
|
||||
(when (and (struct? x) (= :symbol (get x :jolt/type))) (get x :name)))
|
||||
|
|
@ -121,6 +125,21 @@
|
|||
(let [h (or (sym-name (in f 0)) "?") n (sym-name (in f 1))] (if n (string h " " n) h))
|
||||
"?"))
|
||||
|
||||
(defn- require-head? [f]
|
||||
(and (indexed? f) (> (length f) 0)
|
||||
(let [h (sym-name (in f 0))] (and h (or (= h "require") (= h "use"))))))
|
||||
|
||||
(defn- scan-eval-requires! [ctx form]
|
||||
"Recursively eval any (require ...)/(use ...) sub-form against the ctx so the
|
||||
alias registers + the aliased ns loads BEFORE the AOT analyzer resolves its
|
||||
qualified refs — the whole user form is analyzed up front, before any require
|
||||
would run at eval time (jolt-nfca). Failures are swallowed (the ref then stays
|
||||
an emit-err, the prior behavior)."
|
||||
(when (indexed? form)
|
||||
(if (require-head? form)
|
||||
(protect (api/eval-one ctx form))
|
||||
(each sub form (scan-eval-requires! ctx sub)))))
|
||||
|
||||
(defn emit-core-prelude
|
||||
"Assemble the clojure.core prelude as a Scheme string. `ctx` must be a
|
||||
compile-mode ctx; its current ns is set to clojure.core for the duration.
|
||||
|
|
@ -138,7 +157,17 @@
|
|||
(tctx/create-ns ctx ns-name)
|
||||
(tctx/ctx-set-current-ns ctx ns-name)
|
||||
(each f (parse-all src)
|
||||
(unless (macro-form? f)
|
||||
# Register any aliases this ns depends on before analyzing its forms, so an
|
||||
# aliased ref (e.g. clojure.template's walk/postwalk-replace) resolves at emit
|
||||
# time instead of lowering to an "Unknown class walk" host-static. The ns
|
||||
# form's :require is a keyword-headed clause that scan-eval-requires! (matching
|
||||
# the `require`/`use` symbol heads) doesn't catch, so eval the ns form whole.
|
||||
(def ns-form? (and (indexed? f) (> (length f) 0) (= "ns" (sym-name (in f 0)))))
|
||||
(if ns-form? (protect (api/eval-one ctx f)) (scan-eval-requires! ctx f))
|
||||
# Skip emitting ns forms: their only role here is alias registration, and a
|
||||
# runtime ns-switch would leak into the prelude's trailing *ns* (the def-var!s
|
||||
# already carry explicit ns names). Macros have no runtime value either.
|
||||
(unless (or ns-form? (macro-form? f))
|
||||
(++ total)
|
||||
(def res (protect (emit/emit (backend/analyze-form ctx f))))
|
||||
(when (res 0)
|
||||
|
|
@ -179,21 +208,6 @@
|
|||
"(set-chez-ns! \"user\")\n"
|
||||
"(printf \"~a\\n\" (jolt-final-str " final-scm "))\n"))
|
||||
|
||||
(defn- require-head? [f]
|
||||
(and (indexed? f) (> (length f) 0)
|
||||
(let [h (sym-name (in f 0))] (and h (or (= h "require") (= h "use"))))))
|
||||
|
||||
(defn- scan-eval-requires! [ctx form]
|
||||
"Recursively eval any (require ...)/(use ...) sub-form against the ctx so the
|
||||
alias registers + the aliased ns loads BEFORE the AOT analyzer resolves its
|
||||
qualified refs — the whole user form is analyzed up front, before any require
|
||||
would run at eval time (jolt-nfca). Failures are swallowed (the ref then stays
|
||||
an emit-err, the prior behavior)."
|
||||
(when (indexed? form)
|
||||
(if (require-head? form)
|
||||
(protect (api/eval-one ctx form))
|
||||
(each sub form (scan-eval-requires! ctx sub)))))
|
||||
|
||||
(defn eval-e-with-prelude
|
||||
"Run a single user expression `src` on Chez with the full clojure.core prelude
|
||||
(loaded from `prelude-path`). Emits `src` in prelude mode so any core ref
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@
|
|||
"host/chez/natives-str.ss" "host/chez/records.ss"
|
||||
"host/chez/host-class.ss"
|
||||
"host/chez/host-static.ss" "host/chez/dot-forms.ss"
|
||||
"src/jolt/clojure/string.clj"]
|
||||
"src/jolt/clojure/string.clj" "src/jolt/clojure/walk.clj"
|
||||
"src/jolt/clojure/template.clj"]
|
||||
(array/push parts (slurp f)))
|
||||
(string/slice (string (hash (string/join parts))) 0))
|
||||
|
||||
|
|
|
|||
|
|
@ -8,11 +8,15 @@
|
|||
;; seed predicates are simply absent here for now.
|
||||
|
||||
(define (jolt-map? x) (pmap? x))
|
||||
;; a map entry is a pvec under the hood but is NOT vector? (matches Clojure's
|
||||
;; MapEntry — jolt-agw6); the public vector? predicate excludes it.
|
||||
(define (jolt-vector? x) (and (pvec? x) (not (pvec-ent x))))
|
||||
;; a map entry is a pvec under the hood AND is vector? — Clojure's MapEntry
|
||||
;; implements IPersistentVector, so (vector? (first {:a 1})) is true (the seed
|
||||
;; agrees; jolt-75sv corrected the earlier exclusion).
|
||||
(define (jolt-vector? x) (pvec? x))
|
||||
(define (jolt-set? x) (pset? x))
|
||||
(define (jolt-seq? x) (or (cseq? x) (empty-list-t? x)))
|
||||
;; (list? x): a list-marked cseq node or the empty list (). A lazy/vector-backed
|
||||
;; seq, (rest list), (seq coll), (map …) are seqs but not lists (jolt-75sv).
|
||||
(define (jolt-list-pred? x) (or (and (cseq? x) (cseq-list? x)) (empty-list-t? x)))
|
||||
(define (jolt-coll-pred? x)
|
||||
(or (pvec? x) (pmap? x) (pset? x) (cseq? x) (empty-list-t? x)))
|
||||
(define (jolt-number? x) (number? x))
|
||||
|
|
@ -52,6 +56,7 @@
|
|||
(def-var! "clojure.core" "vector?" jolt-vector?)
|
||||
(def-var! "clojure.core" "set?" jolt-set?)
|
||||
(def-var! "clojure.core" "seq?" jolt-seq?)
|
||||
(def-var! "clojure.core" "list?" jolt-list-pred?)
|
||||
(def-var! "clojure.core" "coll?" jolt-coll-pred?)
|
||||
(def-var! "clojure.core" "fn?" jolt-fn?)
|
||||
(def-var! "clojure.core" "boolean?" jolt-boolean-pred?)
|
||||
|
|
|
|||
|
|
@ -18,9 +18,16 @@
|
|||
;; head : the realized first element. tail : EITHER a realized seq (cseq |
|
||||
;; jolt-nil) when forced? is #t, OR a 0-arg thunk producing one when forced? is
|
||||
;; #f. Forcing memoizes (set the tail to the produced seq, flip forced?).
|
||||
(define-record-type cseq (fields head (mutable tail) (mutable forced?)) (nongenerative chez-cseq-v1))
|
||||
(define (cseq-realized head tail) (make-cseq head tail #t)) ; tail already a seq
|
||||
(define (cseq-lazy head tail-thunk) (make-cseq head tail-thunk #f))
|
||||
;; list? : #t when this cell is a PersistentList node (list literal / (list ...)
|
||||
;; / cons / reverse / conj-onto-list) vs a lazy or vector-backed seq cell — the
|
||||
;; only thing that distinguishes a list from any other realized seq on this host,
|
||||
;; since one record type backs both (clojure.core/list? — jolt-75sv). The marker
|
||||
;; lives on the cell, so (rest a-list) / (seq a-vector) / (map …) yield plain seq
|
||||
;; cells and are not list?, matching the seed.
|
||||
(define-record-type cseq (fields head (mutable tail) (mutable forced?) list?) (nongenerative chez-cseq-v2))
|
||||
(define (cseq-realized head tail) (make-cseq head tail #t #f)) ; tail already a seq
|
||||
(define (cseq-lazy head tail-thunk) (make-cseq head tail-thunk #f #f))
|
||||
(define (cseq-list head tail) (make-cseq head tail #t #t)) ; a PersistentList node
|
||||
(define (seq-first s) (cseq-head s))
|
||||
(define (seq-more s) ; force the tail; returns a seq (cseq | jolt-nil)
|
||||
(if (cseq-forced? s) (cseq-tail s)
|
||||
|
|
@ -72,11 +79,26 @@
|
|||
(let ((m (seq-more s))) (if (jolt-nil? m) jolt-empty-list m)))))
|
||||
(define (jolt-next x) ; nil when the rest is empty
|
||||
(let ((s (jolt-seq x))) (if (jolt-nil? s) jolt-nil (seq-more s))))
|
||||
(define (jolt-cons x coll) (cseq-realized x (jolt-seq coll)))
|
||||
(define (jolt-list . xs) (if (null? xs) jolt-empty-list (list->cseq xs)))
|
||||
(define (jolt-reverse coll) (let loop ((s (jolt-seq coll)) (acc jolt-empty-list))
|
||||
(if (jolt-nil? s) acc
|
||||
(loop (jolt-seq (seq-more s)) (cseq-realized (seq-first s) (if (empty-list-t? acc) jolt-nil acc))))))
|
||||
;; Only the HEAD cell carries the list marker — (rest a-list)/(next a-list) return
|
||||
;; the unmarked tail, so they are seqs and not list?, matching the seed (which
|
||||
;; makes rest-of-a-list a non-list seq). cons/list/reverse/conj therefore mark
|
||||
;; just the cell they create.
|
||||
;;
|
||||
;; cons always yields a list — (list? (cons x anything)) is true on the seed (cons
|
||||
;; onto a vector/seq/nil all report list?).
|
||||
(define (jolt-cons x coll) (cseq-list x (jolt-seq coll)))
|
||||
;; Scheme list -> a jolt PersistentList: head is a list cell, the tail chain is
|
||||
;; plain seq cells. For (list …) and quoted list literals (the emitter lowers
|
||||
;; '(a b) to (jolt-list a b)).
|
||||
(define (jolt-list . xs)
|
||||
(if (null? xs) jolt-empty-list (cseq-list (car xs) (list->cseq (cdr xs)))))
|
||||
;; reverse yields a list (seed: (list? (reverse coll)) is always true). Build a
|
||||
;; plain seq chain, then mark its head as a list cell.
|
||||
(define (jolt-reverse coll)
|
||||
(let loop ((s (jolt-seq coll)) (acc jolt-empty-list))
|
||||
(if (jolt-nil? s)
|
||||
(if (empty-list-t? acc) acc (cseq-list (seq-first acc) (seq-more acc)))
|
||||
(loop (jolt-seq (seq-more s)) (cseq-realized (seq-first s) (if (empty-list-t? acc) jolt-nil acc))))))
|
||||
(define (jolt-last coll) (let loop ((s (jolt-seq coll)) (last jolt-nil))
|
||||
(if (jolt-nil? s) last (loop (jolt-seq (seq-more s)) (seq-first s)))))
|
||||
;; nth over a seq (walks; forces lazily). default? selects the 3-arg behavior.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue