From 10e3a007774739d389ae38eab2c0046fe21a32f4 Mon Sep 17 00:00:00 2001 From: Yogthos Date: Mon, 22 Jun 2026 03:20:31 -0400 Subject: [PATCH] Host completeness for the ring stack (ring-app) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gaps surfaced loading ring-core / hiccup / config / tools.logging on Chez: - clojure.java.io/resource — resolve a named resource against the loader's source roots (no classpath), returning a slurp-able File. - (Object.) constructor — a fresh distinct value (lock / unique sentinel). - *out* / *err* as dynamic vars over a port-writer, so (binding [*out* *err*] …) and #'*out* compile and run (tools.logging, selmer). - :refer :all now registers a refer-all relation, so an unqualified var from a (require '[ns :refer :all]) resolves at compile time — including #'var. - java.lang.Character interop ((.toString \+) etc.). - StringReader accepts a char[] ((StringReader. (char-array s))), not just a String. - the \p{L} translation stops just below the UTF-16 surrogate gap (\x{D7FF}) instead of \x{10FFFF} — a range across the surrogates made irregex's char-set construction call integer->char on a surrogate and crash. --- host/chez/host-static.ss | 26 +++++++++++++++++++++++++- host/chez/io.ss | 11 +++++++++++ host/chez/ns.ss | 12 ++++++++---- host/chez/records.ss | 9 +++++++++ host/chez/regex.ss | 9 ++++++--- 5 files changed, 59 insertions(+), 8 deletions(-) diff --git a/host/chez/host-static.ss b/host/chez/host-static.ss index 46a2526..4dab742 100644 --- a/host/chez/host-static.ss +++ b/host/chez/host-static.ss @@ -267,6 +267,10 @@ (define (render-piece x) (cond ((jolt-nil? x) "null") ((char? x) (string x)) ((string? x) x) (else (jolt-str-render-one x)))) +;; (Object.) — a fresh value with distinct identity (libraries use it as a lock +;; or a unique sentinel). Each call returns a new jhost so identical?/= separate. +(register-class-ctor! "Object" (lambda _ (make-jhost "object" (vector)))) + (register-class-ctor! "StringBuilder" (lambda args (make-jhost "string-builder" ;; a numeric first arg is a CAPACITY hint, not content. @@ -308,6 +312,19 @@ (cons "close" (lambda (self) (fw-flush! self) jolt-nil)) (cons "toString" (lambda (self) (fw-buf self))))) +;; a writer over a real Chez port — the values *out* / *err* hold. write/append +;; push to the port (so (.write *out* s) and (binding [*out* *err*] …) work); +;; it isn't a buffer, so toString is empty. Lets libraries that touch *out*/*err* +;; (tools.logging, selmer) compile and run. +(register-host-methods! "port-writer" + (list (cons "write" (lambda (self x) (display (writer-piece x) (vector-ref (jhost-state self) 0)) jolt-nil)) + (cons "append" (lambda (self x) (display (render-piece x) (vector-ref (jhost-state self) 0)) self)) + (cons "flush" (lambda (self) (flush-output-port (vector-ref (jhost-state self) 0)) jolt-nil)) + (cons "close" (lambda (self) jolt-nil)) + (cons "toString" (lambda (self) "")))) +(def-var! "clojure.core" "*out*" (make-jhost "port-writer" (vector (current-output-port)))) +(def-var! "clojure.core" "*err*" (make-jhost "port-writer" (vector (current-error-port)))) + ;; ---- java.util.HashMap ------------------------------------------------------ ;; A mutable map keyed by jolt values (jolt-hash / jolt=2). State #(chez-hashtable). ;; Constructors: () | (capacity) | (capacity load-factor) [sizing args ignored] | @@ -360,7 +377,14 @@ ;; ---- StringReader ----------------------------------------------------------- ;; state: a vector #(string pos marked). (register-class-ctor! "StringReader" - (lambda (src . _) (make-jhost "string-reader" (vector (if (string? src) src (jolt-str-render-one src)) 0 0)))) + ;; src is a String or a char[] ((StringReader. (char-array s)) — selmer's parser + ;; reads templates this way); a char-array becomes the string of its chars. + (lambda (src . _) + (make-jhost "string-reader" + (vector (cond ((string? src) src) + ((jolt-array? src) (apply string-append (map jolt-str-render-one (seq->list (jolt-seq src))))) + (else (jolt-str-render-one src))) + 0 0)))) (define (sr-s self) (vector-ref (jhost-state self) 0)) (define (sr-pos self) (vector-ref (jhost-state self) 1)) (define (sr-pos! self p) (vector-set! (jhost-state self) 1 p)) diff --git a/host/chez/io.ss b/host/chez/io.ss index 2367099..56cc9f6 100644 --- a/host/chez/io.ss +++ b/host/chez/io.ss @@ -263,4 +263,15 @@ (def-var! "clojure.java.io" "writer" jolt-io-writer) (def-var! "clojure.java.io" "input-stream" jolt-io-reader) (def-var! "clojure.java.io" "output-stream" jolt-io-writer) +;; resource: jolt has no classpath, so a named resource is resolved against the +;; loader's source roots (a project's :paths, e.g. "resources"). Returns a File +;; (slurp/reader-able) for the first match, else nil. get-source-roots is the +;; loader's accessor (loader.ss), resolved at call time — the runtime CLI loads it. +(define (jolt-io-resource name) + (let ((nm (jolt-str-render-one name))) + (let loop ((roots (get-source-roots))) + (cond ((null? roots) jolt-nil) + ((file-exists? (string-append (car roots) "/" nm)) (make-jfile (string-append (car roots) "/" nm))) + (else (loop (cdr roots))))))) +(def-var! "clojure.java.io" "resource" jolt-io-resource) (def-var! "clojure.java.io" "as-url" (lambda (x) (if (and (jhost? x) (string=? (jhost-tag x) "url")) x (make-url (jolt-str-render-one x))))) diff --git a/host/chez/ns.ss b/host/chez/ns.ss index 4f02c25..e20e0f2 100644 --- a/host/chez/ns.ss +++ b/host/chez/ns.ss @@ -68,10 +68,14 @@ ((string=? (keyword-t-name k) "as") (when (symbol-t? v) (chez-register-alias! cns (symbol-t-name v) target))) ((string=? (keyword-t-name k) "refer") - (when (pvec? v) - (for-each (lambda (n) - (when (symbol-t? n) (chez-register-refer! cns (symbol-t-name n) target))) - (seq->list v))))))) + (cond + ;; :refer :all — bring in every public var (require :refer :all) + ((and (keyword? v) (string=? (keyword-t-name v) "all")) + (chez-register-refer-all! cns target)) + ((pvec? v) + (for-each (lambda (n) + (when (symbol-t? n) (chez-register-refer! cns (symbol-t-name n) target))) + (seq->list v)))))))) (loop (cddr xs)))))))) ;; a namespace designator -> its name string (a jns or a symbol; the corpus never diff --git a/host/chez/records.ss b/host/chez/records.ss index b9a954e..5e08faf 100644 --- a/host/chez/records.ss +++ b/host/chez/records.ss @@ -266,6 +266,15 @@ (let ((v (jolt-first s))) (jiterator-cur-set! obj (jolt-rest s)) v)))) (else (error #f (string-append "No method " method-name " on Iterator"))))) ((string=? method-name "iterator") (make-jiterator (jolt-seq obj))) + ;; java.lang.Character interop: (.toString \+) -> "+", etc. + ((char? obj) + (cond ((string=? method-name "toString") (string obj)) + ((string=? method-name "charValue") obj) + ((string=? method-name "hashCode") (char->integer obj)) + ((string=? method-name "equals") (and (pair? rest) (char? (car rest)) (char=? obj (car rest)))) + ((string=? method-name "compareTo") + (let ((o (car rest))) (cond ((char? obj o) 1) (else 0)))) + (else (error #f (string-append "No method " method-name " on char"))))) (else (error #f (string-append "No method " method-name " for value: " (jolt-pr-str obj))))))) diff --git a/host/chez/regex.ss b/host/chez/regex.ss index 9e0dd45..ccd0938 100644 --- a/host/chez/regex.ss +++ b/host/chez/regex.ss @@ -42,9 +42,12 @@ ;; ORIGINAL source is kept for printing; only the compiled pattern is translated. (define (prop-class name) (cond - ;; L/Alpha: ASCII letters + any non-ASCII codepoint (UTF-8 high - ;; bytes count as letters, so ^\p{L}+$ accepts accented words). N/Z stay ASCII-only. - ((or (string=? name "L") (string=? name "Alpha")) "a-zA-Z\\x80-\\x{10FFFF}") + ;; L/Alpha: ASCII letters + non-ASCII up to just below the UTF-16 surrogate gap + ;; (D800). This covers essentially every real letter (Latin/Greek/Cyrillic/CJK/… + ;; live below D800); the supplementary planes above it are rare and a range that + ;; reaches them makes irregex's char-set construction call integer->char on a + ;; surrogate and crash. N/Z stay ASCII-only. + ((or (string=? name "L") (string=? name "Alpha")) "a-zA-Z\\x80-\\x{D7FF}") ((string=? name "Lu") "A-Z") ((string=? name "Ll") "a-z") ((or (string=? name "N") (string=? name "Nd") (string=? name "Digit")) "0-9")