More library-compat fixes from porting the examples (markdown, malli)

Reader / loader:
- #?@ splicing reader conditionals now actually splice the matched collection's
  items into the enclosing sequence; the splice flag was read but ignored, so a
  binding vector like [a #?@(:clj [b (.foo b)])] lost its alignment.
- the file loader reads by position and skips a top-level form that reads as
  nothing (a :cljs-only #?, a #_ discard, a trailing comment) instead of
  treating it as EOF — which silently dropped the rest of a large .cljc file.
- jolt's reader feature set now includes :clj (was {:jolt :default}). jolt is a
  Clojure/JVM-compatible host that emulates clojure.lang.* and java.* interop,
  so it reads the :clj branch of a .cljc library, not :cljs. This also lets four
  more reader-conditional corpus cases pass (floor 2726 -> 2730).

Backend:
- munge-name escapes ' (prime) -> _PRIME_; a Clojure symbol like f' otherwise
  emitted a bare ' into Scheme, which is the quote reader macro and unbalanced
  the output.

Host shims:
- clojure.java.io/writer (pass through a StringWriter, file-back a path) and a
  readLine on the string reader, so line-seq over (io/reader …) works (markdown).

A better "unsupported destructuring pattern: <pat>" error message.
This commit is contained in:
Yogthos 2026-06-22 02:25:11 -04:00
parent 7e2704642b
commit 2de0543613
9 changed files with 93 additions and 18 deletions

View file

@ -33,6 +33,10 @@
(define rdr-eof (list 'reader-eof))
(define (rdr-eof? x) (eq? x rdr-eof))
;; A splicing reader conditional #?@(...) yields this wrapper; the enclosing
;; sequence reader splices its items in place (never a legal jolt value).
(define-record-type rdr-splice-t (fields items) (nongenerative rdr-splice-v1))
(define (rdr-ws? c)
(or (char-whitespace? c) (char=? c #\,)))
@ -248,9 +252,11 @@
((char=? (string-ref s i) close) (values (reverse acc) (+ i 1)))
(else
(let-values (((form j) (rdr-read-form s i end)))
(if (rdr-eof? form)
(loop j acc) ; a #_ discard or close — re-check at j
(loop j (cons form acc)))))))))
(cond
((rdr-eof? form) (loop j acc)) ; a #_ discard or no-match #? — re-check at j
((rdr-splice-t? form) ; #?@ — splice the matched collection's items
(loop j (append (reverse (rdr-splice-t-items form)) acc)))
(else (loop j (cons form acc))))))))))
;; Map literals must preserve SOURCE key order so the analyzer emits the value
;; expressions in source order (Clojure guarantees left-to-right map-literal eval).
@ -360,9 +366,13 @@
(if rest-sym (list (jolt-symbol #f "&") rest-sym) '()))))
(values (jolt-list (jolt-symbol #f "fn*") (apply jolt-vector params) body) j))))))
;; reader conditionals (jolt-qjr0): jolt's feature set is {:jolt :default}; the
;; FIRST clause whose feature key is in the set wins (clause order, like Clojure).
(define rdr-features '("jolt" "default"))
;; reader conditionals (jolt-qjr0): jolt's feature set is {:jolt :clj :default};
;; the FIRST clause whose feature key is in the set wins (clause order, like
;; Clojure). jolt is a Clojure/JVM-compatible host — it emulates clojure.lang.*
;; and java.* interop — so it reads the :clj branch of a .cljc library (the JVM
;; code path its host shims target), not the :cljs one. A library can still
;; override with a :jolt-specific branch (place it before :clj).
(define rdr-features '("jolt" "clj" "default"))
(define (rdr-feature? kw)
(and (keyword? kw) (jolt-nil? (let ((n (keyword-t-ns kw))) (if n n jolt-nil)))
(and (member (keyword-t-name kw) rdr-features) #t)))
@ -376,7 +386,17 @@
(else '()))))
(let loop ((xs items))
(cond ((or (null? xs) (null? (cdr xs))) (values rdr-eof j)) ; no match -> discard
((rdr-feature? (car xs)) (values (cadr xs) j))
((rdr-feature? (car xs))
(if splice
;; #?@ — the matched value is a collection whose ITEMS splice
;; into the enclosing sequence (read-seq expands the wrapper).
(let ((v (cadr xs)))
(values (make-rdr-splice-t
(cond ((pvec? v) (seq->list v))
((or (cseq? v) (empty-list-t? v)) (seq->list v))
(else (list v))))
j))
(values (cadr xs) j)))
(else (loop (cddr xs)))))))))
(define (rdr-read-dispatch s i end) ; i points just past the '#'