Fix conformance gaps: exception types, byte/getBytes, host classes
Shake-out from the conformance-library sweep. Host-side fixes (runtime .ss, no re-mint) plus one analyzer change (re-minted): - Exception fidelity: ex-info and host-constructed throwables (RuntimeException. etc.) now carry their JVM class, so (class e), instance? across the exception hierarchy, .getMessage, and clojure.test thrown?/thrown-with-msg? all work. - .getBytes returns a seqable/countable byte-array and honors UTF-16/UTF-32; String. decodes them. ->bytevector accepts byte-arrays (Base64). - Universal .getClass / .toString / .indexOf / .lastIndexOf on any value/seq. - record? uses the host jrec? predicate (the old (get x :jolt/deftype) crashed on a sorted-map by invoking its comparator). - extend-protocol to abstract host types (clojure.lang.Fn/IFn/APersistentVector, java.net.URI) dispatches. - New host classes: clojure.lang.PersistentQueue, java.util.ArrayList, java.net.URI, java.io.File / java.util.UUID ctors, Double/Float ctors+statics, regex instance? Pattern, System/setProperty. - *assert* / *print-readably* are real settable/bindable vars. - (symbol "ns/name") splits the namespace at the last slash. - letfn fn params desugar destructuring (analyzer; re-minted). unit.edn gains exinfo/hostobj/queue/hostctor/destructure regression rows.
This commit is contained in:
parent
185b4fd3ca
commit
d83175b8c2
14 changed files with 570 additions and 39 deletions
|
|
@ -70,10 +70,20 @@
|
||||||
(let ((a (car args)))
|
(let ((a (car args)))
|
||||||
(cond
|
(cond
|
||||||
((jolt-symbol? a) a)
|
((jolt-symbol? a) a)
|
||||||
;; no-ns sentinel is #f — matches emit's quoted-symbol lowering
|
;; (symbol "ns/name") splits the namespace at the LAST "/" (JVM
|
||||||
;; (jolt-symbol #f "x"), so (= 'x (symbol "x")) holds (jolt= compares ns
|
;; Symbol.intern), so (namespace (symbol "foo/bar")) => "foo". A lone "/"
|
||||||
;; with strict equal?; jolt-nil vs #f would otherwise differ).
|
;; or a leading slash has no namespace. The no-ns sentinel is #f — matches
|
||||||
((string? a) (jolt-symbol #f a))
|
;; emit's quoted-symbol lowering (jolt-symbol #f "x"), so (= 'x (symbol
|
||||||
|
;; "x")) holds (jolt= compares ns with strict equal?).
|
||||||
|
((string? a)
|
||||||
|
(let ((slen (string-length a)))
|
||||||
|
(if (string=? a "/")
|
||||||
|
(jolt-symbol #f "/")
|
||||||
|
(let loop ((i (- slen 1)))
|
||||||
|
(cond ((<= i 0) (jolt-symbol #f a))
|
||||||
|
((char=? (string-ref a i) #\/)
|
||||||
|
(jolt-symbol (substring a 0 i) (substring a (+ i 1) slen)))
|
||||||
|
(else (loop (- i 1))))))))
|
||||||
((keyword? a) (jolt-symbol (keyword-t-ns a) (keyword-t-name a)))
|
((keyword? a) (jolt-symbol (keyword-t-ns a) (keyword-t-name a)))
|
||||||
(else (error #f "symbol: requires string/symbol" a)))))
|
(else (error #f "symbol: requires string/symbol" a)))))
|
||||||
((= (length args) 2) (jolt-symbol (car args) (cadr args)))
|
((= (length args) 2) (jolt-symbol (car args) (cadr args)))
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,9 @@
|
||||||
(substring method-name 1 (string-length method-name))
|
(substring method-name 1 (string-length method-name))
|
||||||
method-name)))
|
method-name)))
|
||||||
(cond
|
(cond
|
||||||
|
;; (.getClass x) universal — the class token for any value, before the
|
||||||
|
;; collection/map field-lookup arms below would read it as a missing key.
|
||||||
|
((string=? method-name "getClass") (jolt-class obj))
|
||||||
;; collection interop first (entry count / seq / nth / get / containsKey).
|
;; collection interop first (entry count / seq / nth / get / containsKey).
|
||||||
((and (dot-coll? obj) (dot-coll-method obj mname rest))
|
((and (dot-coll? obj) (dot-coll-method obj mname rest))
|
||||||
=> (lambda (box) (car box)))
|
=> (lambda (box) (car box)))
|
||||||
|
|
|
||||||
|
|
@ -16,3 +16,10 @@
|
||||||
;; *warn-on-reflection* — jolt has no reflection, so the var reads false; (set!
|
;; *warn-on-reflection* — jolt has no reflection, so the var reads false; (set!
|
||||||
;; *warn-on-reflection* …) resolves and updates it (a no-op effect).
|
;; *warn-on-reflection* …) resolves and updates it (a no-op effect).
|
||||||
(def-var! "clojure.core" "*warn-on-reflection*" #f)
|
(def-var! "clojure.core" "*warn-on-reflection*" #f)
|
||||||
|
|
||||||
|
;; *assert* — gates `assert`; settable/bindable (malli.assert toggles it). Default
|
||||||
|
;; true, like the JVM.
|
||||||
|
(def-var! "clojure.core" "*assert*" #t)
|
||||||
|
|
||||||
|
;; *print-readably* — bound by pr-family / with-out-str-style code; default true.
|
||||||
|
(def-var! "clojure.core" "*print-readably*" #t)
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,12 @@
|
||||||
((symbol-t? x) "clojure.lang.Symbol")
|
((symbol-t? x) "clojure.lang.Symbol")
|
||||||
((jolt-atom? x) "clojure.lang.Atom")
|
((jolt-atom? x) "clojure.lang.Atom")
|
||||||
((char? x) "java.lang.Character")
|
((char? x) "java.lang.Character")
|
||||||
|
((regex-t? x) "java.util.regex.Pattern")
|
||||||
((procedure? x) "clojure.lang.IFn")
|
((procedure? x) "clojure.lang.IFn")
|
||||||
|
;; an exception value (ex-info / host-constructed throwable) reports its JVM
|
||||||
|
;; class, so (= clojure.lang.ExceptionInfo (class e)) and clojure.test's
|
||||||
|
;; (thrown? Class …) match (records.ss ex-info-map?/ex-info-class).
|
||||||
|
((ex-info-map? x) (ex-info-class x))
|
||||||
(else (jolt-str-render-one (jolt-type x)))))
|
(else (jolt-str-render-one (jolt-type x)))))
|
||||||
|
|
||||||
(def-var! "clojure.core" "class" jolt-class)
|
(def-var! "clojure.core" "class" jolt-class)
|
||||||
|
|
@ -59,6 +64,18 @@
|
||||||
("SocketTimeoutException" . "java.net.SocketTimeoutException")
|
("SocketTimeoutException" . "java.net.SocketTimeoutException")
|
||||||
("MalformedURLException" . "java.net.MalformedURLException")
|
("MalformedURLException" . "java.net.MalformedURLException")
|
||||||
("SSLException" . "javax.net.ssl.SSLException")
|
("SSLException" . "javax.net.ssl.SSLException")
|
||||||
|
("ExceptionInfo" . "clojure.lang.ExceptionInfo")
|
||||||
|
("IExceptionInfo" . "clojure.lang.IExceptionInfo")
|
||||||
|
("Pattern" . "java.util.regex.Pattern")
|
||||||
|
("URI" . "java.net.URI") ("UUID" . "java.util.UUID")
|
||||||
|
("ArrayList" . "java.util.ArrayList") ("PersistentQueue" . "clojure.lang.PersistentQueue")
|
||||||
|
("NumberFormatException" . "java.lang.NumberFormatException")
|
||||||
|
("ArithmeticException" . "java.lang.ArithmeticException")
|
||||||
|
("NullPointerException" . "java.lang.NullPointerException")
|
||||||
|
("ClassCastException" . "java.lang.ClassCastException")
|
||||||
|
("IndexOutOfBoundsException" . "java.lang.IndexOutOfBoundsException")
|
||||||
|
("UnsupportedEncodingException" . "java.io.UnsupportedEncodingException")
|
||||||
|
("FileNotFoundException" . "java.io.FileNotFoundException")
|
||||||
("Throwable" . "java.lang.Throwable")))
|
("Throwable" . "java.lang.Throwable")))
|
||||||
(for-each
|
(for-each
|
||||||
(lambda (pair) (def-var! "clojure.core" (car pair) (cdr pair)))
|
(lambda (pair) (def-var! "clojure.core" (car pair) (cdr pair)))
|
||||||
|
|
@ -90,4 +107,12 @@
|
||||||
"java.net.UnknownHostException" "java.net.ConnectException"
|
"java.net.UnknownHostException" "java.net.ConnectException"
|
||||||
"java.net.SocketTimeoutException" "java.net.MalformedURLException"
|
"java.net.SocketTimeoutException" "java.net.MalformedURLException"
|
||||||
"javax.net.ssl.SSLException"
|
"javax.net.ssl.SSLException"
|
||||||
|
"java.lang.NumberFormatException" "java.lang.ArithmeticException"
|
||||||
|
"java.lang.NullPointerException" "java.lang.ClassCastException"
|
||||||
|
"java.lang.IndexOutOfBoundsException" "java.io.FileNotFoundException"
|
||||||
|
"java.io.UnsupportedEncodingException"
|
||||||
|
;; clojure.lang.ExceptionInfo / IExceptionInfo compared against (class e)
|
||||||
|
"clojure.lang.ExceptionInfo" "clojure.lang.IExceptionInfo"
|
||||||
|
"java.util.regex.Pattern" "java.net.URI" "java.util.UUID"
|
||||||
|
"clojure.lang.PersistentQueue"
|
||||||
"clojure.lang.Keyword" "clojure.lang.Symbol" "clojure.lang.Ratio" "clojure.lang.Atom"))
|
"clojure.lang.Keyword" "clojure.lang.Symbol" "clojure.lang.Ratio" "clojure.lang.Atom"))
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,9 @@
|
||||||
(set! record-method-dispatch
|
(set! record-method-dispatch
|
||||||
(lambda (obj method-name rest-args)
|
(lambda (obj method-name rest-args)
|
||||||
(cond
|
(cond
|
||||||
|
;; (.getClass x) is universal — the class token for any value (incl. numbers
|
||||||
|
;; / jhost) — before the per-type arms that would otherwise reject it.
|
||||||
|
((string=? method-name "getClass") (jolt-class obj))
|
||||||
((jhost? obj)
|
((jhost? obj)
|
||||||
(let ((mh (hashtable-ref host-methods-tbl (jhost-tag obj) #f)))
|
(let ((mh (hashtable-ref host-methods-tbl (jhost-tag obj) #f)))
|
||||||
(let ((f (and mh (hashtable-ref mh method-name #f))))
|
(let ((f (and mh (hashtable-ref mh method-name #f))))
|
||||||
|
|
@ -121,6 +124,21 @@
|
||||||
(if (string? s) s (jolt-str-render-one s)) "\""))))
|
(if (string? s) s (jolt-str-render-one s)) "\""))))
|
||||||
(define (char-code c) (if (char? c) (char->integer c) (jnum->exact c)))
|
(define (char-code c) (if (char? c) (char->integer c) (jnum->exact c)))
|
||||||
|
|
||||||
|
;; parse a double string (Double/parseDouble, (Double. s)); JVM accepts NaN /
|
||||||
|
;; Infinity / decimal / scientific. #f on failure.
|
||||||
|
(define (parse-double-str s)
|
||||||
|
(let ((t (str-trim (if (string? s) s (jolt-str-render-one s)))))
|
||||||
|
(cond
|
||||||
|
((or (string=? t "NaN") (string=? t "+NaN") (string=? t "-NaN")) +nan.0)
|
||||||
|
((or (string=? t "Infinity") (string=? t "+Infinity")) +inf.0)
|
||||||
|
((string=? t "-Infinity") -inf.0)
|
||||||
|
(else (let ((n (string->number t))) (and n (real? n) (exact->inexact n)))))))
|
||||||
|
(define (parse-double-or-throw s)
|
||||||
|
(or (parse-double-str s)
|
||||||
|
(error #f (string-append "NumberFormatException: For input string: \""
|
||||||
|
(if (string? s) s (jolt-str-render-one s)) "\""))))
|
||||||
|
(define (->double x) (if (number? x) (exact->inexact x) (parse-double-or-throw x)))
|
||||||
|
|
||||||
;; ---- java.lang statics ------------------------------------------------------
|
;; ---- java.lang statics ------------------------------------------------------
|
||||||
;; java.lang.Math: sqrt/pow/floor/ceil/trig/log/exp always return a DOUBLE on the
|
;; java.lang.Math: sqrt/pow/floor/ceil/trig/log/exp always return a DOUBLE on the
|
||||||
;; JVM (Chez's sqrt/expt return EXACT for exact args, e.g. (sqrt 9) -> 3), so coerce
|
;; JVM (Chez's sqrt/expt return EXACT for exact args, e.g. (sqrt 9) -> 3), so coerce
|
||||||
|
|
@ -198,6 +216,8 @@
|
||||||
(cons "exit" (lambda args (exit (if (null? args) 0 (jnum->exact (car args))))))
|
(cons "exit" (lambda args (exit (if (null? args) 0 (jnum->exact (car args))))))
|
||||||
;; wrapped in lambdas: the helpers are defined below, resolved at call time.
|
;; wrapped in lambdas: the helpers are defined below, resolved at call time.
|
||||||
(cons "getProperty" (lambda (k . d) (apply sys-get-property k d)))
|
(cons "getProperty" (lambda (k . d) (apply sys-get-property k d)))
|
||||||
|
(cons "setProperty" (lambda (k v) (sys-set-property k v)))
|
||||||
|
(cons "clearProperty" (lambda (k) (sys-clear-property k)))
|
||||||
(cons "getProperties" (lambda () (sys-properties-map)))
|
(cons "getProperties" (lambda () (sys-properties-map)))
|
||||||
(cons "getenv" (lambda k (apply sys-getenv k)))))
|
(cons "getenv" (lambda k (apply sys-getenv k)))))
|
||||||
|
|
||||||
|
|
@ -218,6 +238,19 @@
|
||||||
(list (cons "parseBoolean" (lambda (s) (string=? "true" (ascii-string-down (if (string? s) s (jolt-str-render-one s))))))
|
(list (cons "parseBoolean" (lambda (s) (string=? "true" (ascii-string-down (if (string? s) s (jolt-str-render-one s))))))
|
||||||
(cons "TRUE" #t) (cons "FALSE" #f)))
|
(cons "TRUE" #t) (cons "FALSE" #f)))
|
||||||
|
|
||||||
|
(register-class-ctor! "Double" ->double)
|
||||||
|
(register-class-ctor! "Float" ->double)
|
||||||
|
(register-class-statics! "Double"
|
||||||
|
(list (cons "parseDouble" parse-double-or-throw)
|
||||||
|
(cons "valueOf" ->double)
|
||||||
|
(cons "toString" (lambda (x) (jolt-str-render-one (->double x))))
|
||||||
|
(cons "isNaN" (lambda (x) (and (flonum? x) (nan? x))))
|
||||||
|
(cons "isInfinite" (lambda (x) (and (flonum? x) (infinite? x))))
|
||||||
|
(cons "MAX_VALUE" 1.7976931348623157e308) (cons "MIN_VALUE" 4.9e-324)
|
||||||
|
(cons "POSITIVE_INFINITY" +inf.0) (cons "NEGATIVE_INFINITY" -inf.0) (cons "NaN" +nan.0)))
|
||||||
|
(register-class-statics! "Float"
|
||||||
|
(list (cons "parseFloat" parse-double-or-throw) (cons "valueOf" ->double)))
|
||||||
|
|
||||||
;; Character: ASCII predicates (the engine is byte/ASCII oriented).
|
;; Character: ASCII predicates (the engine is byte/ASCII oriented).
|
||||||
(register-class-statics! "Character"
|
(register-class-statics! "Character"
|
||||||
(list (cons "isUpperCase" (lambda (c) (let ((n (char-code c))) (and (>= n 65) (<= n 90)))))
|
(list (cons "isUpperCase" (lambda (c) (let ((n (char-code c))) (and (>= n 65) (<= n 90)))))
|
||||||
|
|
@ -251,16 +284,28 @@
|
||||||
(cond ((or (substring-index "osx" m) (substring-index "macos" m)) "Mac OS X")
|
(cond ((or (substring-index "osx" m) (substring-index "macos" m)) "Mac OS X")
|
||||||
((or (substring-index "nt" m) (substring-index "windows" m)) "Windows")
|
((or (substring-index "nt" m) (substring-index "windows" m)) "Windows")
|
||||||
(else "Linux"))))
|
(else "Linux"))))
|
||||||
|
;; runtime-settable system properties (System/setProperty). A set value wins over
|
||||||
|
;; the built-in defaults below; clearProperty removes it.
|
||||||
|
(define sys-prop-table (make-hashtable string-hash string=?))
|
||||||
|
(define (sys-set-property k v)
|
||||||
|
(let ((prev (hashtable-ref sys-prop-table k jolt-nil)))
|
||||||
|
(hashtable-set! sys-prop-table k (if (string? v) v (jolt-str-render-one v)))
|
||||||
|
prev))
|
||||||
|
(define (sys-clear-property k)
|
||||||
|
(let ((prev (hashtable-ref sys-prop-table k jolt-nil)))
|
||||||
|
(hashtable-delete! sys-prop-table k) prev))
|
||||||
(define (sys-get-property k . dflt)
|
(define (sys-get-property k . dflt)
|
||||||
(cond ((string=? k "os.name") sys-os-name)
|
(let ((set-val (hashtable-ref sys-prop-table k #f)))
|
||||||
((string=? k "line.separator") "\n")
|
(cond (set-val set-val)
|
||||||
((string=? k "file.separator") "/")
|
((string=? k "os.name") sys-os-name)
|
||||||
((string=? k "path.separator") ":")
|
((string=? k "line.separator") "\n")
|
||||||
((string=? k "user.dir") (or (getenv "PWD") "."))
|
((string=? k "file.separator") "/")
|
||||||
((string=? k "user.home") (or (getenv "HOME") ""))
|
((string=? k "path.separator") ":")
|
||||||
((string=? k "java.io.tmpdir") (or (getenv "TMPDIR") "/tmp"))
|
((string=? k "user.dir") (or (getenv "PWD") "."))
|
||||||
((pair? dflt) (car dflt))
|
((string=? k "user.home") (or (getenv "HOME") ""))
|
||||||
(else jolt-nil)))
|
((string=? k "java.io.tmpdir") (or (getenv "TMPDIR") "/tmp"))
|
||||||
|
((pair? dflt) (car dflt))
|
||||||
|
(else jolt-nil))))
|
||||||
(define (sys-properties-map)
|
(define (sys-properties-map)
|
||||||
(jolt-hash-map "os.name" sys-os-name "line.separator" "\n" "file.separator" "/"
|
(jolt-hash-map "os.name" sys-os-name "line.separator" "\n" "file.separator" "/"
|
||||||
"user.dir" (or (getenv "PWD") ".") "user.home" (or (getenv "HOME") "")
|
"user.dir" (or (getenv "PWD") ".") "user.home" (or (getenv "HOME") "")
|
||||||
|
|
@ -306,6 +351,52 @@
|
||||||
;; or a unique sentinel). Each call returns a new jhost so identical?/= separate.
|
;; 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! "Object" (lambda _ (make-jhost "object" (vector))))
|
||||||
|
|
||||||
|
;; ---- java.util.ArrayList (jolt-1nnn) ----------------------------------------
|
||||||
|
;; A mutable list backed by a Scheme list in a box. medley's stateful transducers
|
||||||
|
;; (window / partition-between) build one with .add / .size / .toArray / .clear /
|
||||||
|
;; .remove. (ArrayList.) | (ArrayList. n) | (ArrayList. coll).
|
||||||
|
(define (al-list self) (vector-ref (jhost-state self) 0))
|
||||||
|
(define (al-set! self xs) (vector-set! (jhost-state self) 0 xs))
|
||||||
|
(define (make-arraylist xs) (make-jhost "arraylist" (vector xs)))
|
||||||
|
(register-class-ctor! "ArrayList"
|
||||||
|
(lambda args
|
||||||
|
(cond ((null? args) (make-arraylist '()))
|
||||||
|
((number? (car args)) (make-arraylist '())) ; initial capacity, ignored
|
||||||
|
(else (make-arraylist (seq->list (jolt-seq (car args))))))))
|
||||||
|
(register-class-ctor! "java.util.ArrayList"
|
||||||
|
(lambda args
|
||||||
|
(cond ((null? args) (make-arraylist '()))
|
||||||
|
((number? (car args)) (make-arraylist '()))
|
||||||
|
(else (make-arraylist (seq->list (jolt-seq (car args))))))))
|
||||||
|
(define (al-remove-at xs i)
|
||||||
|
(let loop ((xs xs) (i i) (acc '()))
|
||||||
|
(cond ((null? xs) (reverse acc))
|
||||||
|
((= i 0) (append (reverse acc) (cdr xs)))
|
||||||
|
(else (loop (cdr xs) (- i 1) (cons (car xs) acc))))))
|
||||||
|
(register-host-methods! "arraylist"
|
||||||
|
(list
|
||||||
|
(cons "add" (lambda (self . a)
|
||||||
|
;; (.add x) -> append+true; (.add i x) -> insert at i, returns nil.
|
||||||
|
(if (= 1 (length a))
|
||||||
|
(begin (al-set! self (append (al-list self) (list (car a)))) #t)
|
||||||
|
(let ((i (jnum->exact (car a))) (x (cadr a)) (xs (al-list self)))
|
||||||
|
(al-set! self (append (list-head xs i) (list x) (list-tail xs i))) jolt-nil))))
|
||||||
|
(cons "add!" (lambda (self x) (al-set! self (append (al-list self) (list x))) #t))
|
||||||
|
(cons "get" (lambda (self i) (list-ref (al-list self) (jnum->exact i))))
|
||||||
|
(cons "set" (lambda (self i x)
|
||||||
|
(let* ((xs (al-list self)) (idx (jnum->exact i)) (old (list-ref xs idx)))
|
||||||
|
(al-set! self (append (list-head xs idx) (list x) (list-tail xs (+ idx 1)))) old)))
|
||||||
|
(cons "size" (lambda (self) (->num (length (al-list self)))))
|
||||||
|
(cons "isEmpty" (lambda (self) (null? (al-list self))))
|
||||||
|
(cons "remove" (lambda (self i)
|
||||||
|
(let* ((xs (al-list self)) (idx (jnum->exact i)) (old (list-ref xs idx)))
|
||||||
|
(al-set! self (al-remove-at xs idx)) old)))
|
||||||
|
(cons "clear" (lambda (self) (al-set! self '()) jolt-nil))
|
||||||
|
(cons "contains" (lambda (self x) (and (memp (lambda (e) (jolt=2 e x)) (al-list self)) #t)))
|
||||||
|
(cons "toArray" (lambda (self . _) (apply jolt-vector (al-list self))))
|
||||||
|
(cons "iterator" (lambda (self) (make-jiterator (list->cseq (al-list self)))))
|
||||||
|
(cons "toString" (lambda (self) (jolt-pr-str (list->cseq (al-list self)))))))
|
||||||
|
|
||||||
(register-class-ctor! "StringBuilder"
|
(register-class-ctor! "StringBuilder"
|
||||||
(lambda args (make-jhost "string-builder"
|
(lambda args (make-jhost "string-builder"
|
||||||
;; a numeric first arg is a CAPACITY hint, not content.
|
;; a numeric first arg is a CAPACITY hint, not content.
|
||||||
|
|
@ -532,6 +623,12 @@
|
||||||
((or (string=? cs "iso-8859-1") (string=? cs "latin1") (string=? cs "iso8859-1")
|
((or (string=? cs "iso-8859-1") (string=? cs "latin1") (string=? cs "iso8859-1")
|
||||||
(string=? cs "us-ascii") (string=? cs "ascii"))
|
(string=? cs "us-ascii") (string=? cs "ascii"))
|
||||||
(list->string (map integer->char (bytevector->u8-list bv))))
|
(list->string (map integer->char (bytevector->u8-list bv))))
|
||||||
|
((or (string=? cs "utf-16") (string=? cs "utf16") (string=? cs "utf-16be") (string=? cs "unicode"))
|
||||||
|
(utf16->string bv (endianness big))) ; respects a leading BOM
|
||||||
|
((string=? cs "utf-16le") (utf16->string bv (endianness little)))
|
||||||
|
((or (string=? cs "utf-32") (string=? cs "utf32") (string=? cs "utf-32be"))
|
||||||
|
(utf32->string bv (endianness big)))
|
||||||
|
((string=? cs "utf-32le") (utf32->string bv (endianness little)))
|
||||||
(else (guard (e (#t (list->string (map integer->char (bytevector->u8-list bv))))) (utf8->string bv))))))
|
(else (guard (e (#t (list->string (map integer->char (bytevector->u8-list bv))))) (utf8->string bv))))))
|
||||||
(register-class-ctor! "String"
|
(register-class-ctor! "String"
|
||||||
(lambda (x . rest)
|
(lambda (x . rest)
|
||||||
|
|
@ -542,10 +639,27 @@
|
||||||
(register-class-ctor! "BigInteger"
|
(register-class-ctor! "BigInteger"
|
||||||
(lambda (v) (parse-int-or-throw v 10 "BigInteger")))
|
(lambda (v) (parse-int-or-throw v 10 "BigInteger")))
|
||||||
(register-class-ctor! "MapEntry" (lambda (k v) (make-map-entry k v)))
|
(register-class-ctor! "MapEntry" (lambda (k v) (make-map-entry k v)))
|
||||||
;; JVM exception ctors: keep the message string (so getMessage / ex-message work).
|
;; JVM exception ctors -> a typed host throwable carrying the canonical :jolt/class
|
||||||
(for-each (lambda (nm) (register-class-ctor! nm (lambda (msg . _) (if (string? msg) msg (jolt-str-render-one msg)))))
|
;; (so class / instance? / getMessage / ex-message reflect the real type) and the
|
||||||
'("Exception" "RuntimeException" "IllegalArgumentException" "IllegalStateException"
|
;; message. Supports (E. msg), (E. msg cause), (E. cause), and (E.).
|
||||||
"InterruptedException" "UnsupportedOperationException" "IOException"))
|
(for-each
|
||||||
|
(lambda (nm)
|
||||||
|
(let ((canonical (or (resolve-class-hint nm) nm)))
|
||||||
|
(register-class-ctor! nm
|
||||||
|
(lambda args
|
||||||
|
(let* ((a0 (if (pair? args) (car args) jolt-nil))
|
||||||
|
(rest (if (pair? args) (cdr args) '()))
|
||||||
|
(cause (if (pair? rest) (car rest) jolt-nil)))
|
||||||
|
(cond
|
||||||
|
((string? a0) (jolt-host-throwable canonical a0 cause))
|
||||||
|
((jolt-nil? a0) (jolt-host-throwable canonical jolt-nil))
|
||||||
|
;; (E. cause): a lone throwable arg is the cause, message nil.
|
||||||
|
((and (null? rest) (ex-info-map? a0)) (jolt-host-throwable canonical jolt-nil a0))
|
||||||
|
(else (jolt-host-throwable canonical (jolt-str-render-one a0) cause))))))))
|
||||||
|
'("Throwable" "Exception" "RuntimeException" "IllegalArgumentException" "IllegalStateException"
|
||||||
|
"InterruptedException" "UnsupportedOperationException" "IOException" "NumberFormatException"
|
||||||
|
"ArithmeticException" "NullPointerException" "ClassCastException" "IndexOutOfBoundsException"
|
||||||
|
"FileNotFoundException" "UnsupportedEncodingException"))
|
||||||
|
|
||||||
;; ---- URLEncoder / URLDecoder (www-form-urlencoded) --------------------------
|
;; ---- URLEncoder / URLDecoder (www-form-urlencoded) --------------------------
|
||||||
(define (url-unreserved? b)
|
(define (url-unreserved? b)
|
||||||
|
|
@ -589,7 +703,11 @@
|
||||||
|
|
||||||
;; ---- Base64 (RFC 4648) ------------------------------------------------------
|
;; ---- Base64 (RFC 4648) ------------------------------------------------------
|
||||||
(define b64-alphabet "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
|
(define b64-alphabet "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
|
||||||
(define (->bytevector x) (cond ((bytevector? x) x) ((string? x) (string->utf8 x)) (else (string->utf8 (jolt-str-render-one x)))))
|
(define (->bytevector x)
|
||||||
|
(cond ((bytevector? x) x)
|
||||||
|
((and (jolt-array? x) (eq? (jolt-array-kind x) 'byte)) (na-bytearray->bv x))
|
||||||
|
((string? x) (string->utf8 x))
|
||||||
|
(else (string->utf8 (jolt-str-render-one x)))))
|
||||||
(define (b64-encode x)
|
(define (b64-encode x)
|
||||||
(let* ((bs (->bytevector x)) (n (bytevector-length bs)) (out '()))
|
(let* ((bs (->bytevector x)) (n (bytevector-length bs)) (out '()))
|
||||||
(let loop ((i 0))
|
(let loop ((i 0))
|
||||||
|
|
|
||||||
118
host/chez/io.ss
118
host/chez/io.ss
|
|
@ -323,3 +323,121 @@
|
||||||
((htable? x) x)
|
((htable? x) x)
|
||||||
(else (let ((ctor (lookup-class class-ctors-tbl "URL")))
|
(else (let ((ctor (lookup-class class-ctors-tbl "URL")))
|
||||||
(if ctor (ctor (jolt-str-render-one x)) (make-url (jolt-str-render-one x))))))))
|
(if ctor (ctor (jolt-str-render-one x)) (make-url (jolt-str-render-one x))))))))
|
||||||
|
|
||||||
|
;; --- java.io.File / java.util.UUID constructors (jolt-1nnn) ------------------
|
||||||
|
;; (java.io.File. parent child) joins with "/"; (File. path) wraps the path.
|
||||||
|
(register-class-ctor! "File"
|
||||||
|
(lambda (a . rest)
|
||||||
|
(if (pair? rest)
|
||||||
|
(jolt-make-file (string-append (file-path-of a) "/" (file-path-of (car rest))))
|
||||||
|
(jolt-make-file a))))
|
||||||
|
;; UUID: randomUUID / fromString statics + a (UUID. s) string ctor.
|
||||||
|
(register-class-statics! "UUID"
|
||||||
|
(list (cons "randomUUID" (lambda () (jolt-random-uuid)))
|
||||||
|
(cons "fromString" (lambda (s) (jolt-parse-uuid (jolt-str-render-one s))))))
|
||||||
|
(register-class-statics! "java.util.UUID"
|
||||||
|
(list (cons "randomUUID" (lambda () (jolt-random-uuid)))
|
||||||
|
(cons "fromString" (lambda (s) (jolt-parse-uuid (jolt-str-render-one s))))))
|
||||||
|
(register-class-ctor! "UUID" (lambda (s) (jolt-parse-uuid (jolt-str-render-one s))))
|
||||||
|
|
||||||
|
;; --- java.net.URI (jolt-1nnn) -----------------------------------------------
|
||||||
|
;; A minimal RFC-3986 split into scheme/authority/host/port/path/query/fragment,
|
||||||
|
;; kept in a jhost "uri" carrying the original string. (str u)/(.toString u) give
|
||||||
|
;; the original; getHost is nil for a relative URI (hiccup.util/to-str branches on
|
||||||
|
;; it). instance? java.net.URI + extend-protocol dispatch work via value-host-tags.
|
||||||
|
(define (uri-index-of s ch from)
|
||||||
|
(let ((n (string-length s)))
|
||||||
|
(let loop ((i from)) (cond ((>= i n) #f) ((char=? (string-ref s i) ch) i) (else (loop (+ i 1)))))))
|
||||||
|
(define (uri-scheme-end s)
|
||||||
|
;; index of ':' that ends a scheme (letter then alnum/+-. before any /?#), or #f.
|
||||||
|
(let ((n (string-length s)))
|
||||||
|
(and (> n 0) (char-alphabetic? (string-ref s 0))
|
||||||
|
(let loop ((i 1))
|
||||||
|
(cond ((>= i n) #f)
|
||||||
|
((char=? (string-ref s i) #\:) i)
|
||||||
|
((let ((c (string-ref s i)))
|
||||||
|
(or (char-alphabetic? c) (char-numeric? c) (char=? c #\+) (char=? c #\-) (char=? c #\.)))
|
||||||
|
(loop (+ i 1)))
|
||||||
|
(else #f))))))
|
||||||
|
(define (uri-parse s)
|
||||||
|
(let* ((n (string-length s))
|
||||||
|
(se (uri-scheme-end s))
|
||||||
|
(scheme (and se (substring s 0 se)))
|
||||||
|
(rest-start (if se (+ se 1) 0))
|
||||||
|
;; fragment
|
||||||
|
(hash (uri-index-of s #\# rest-start))
|
||||||
|
(frag (and hash (substring s (+ hash 1) n)))
|
||||||
|
(pre-frag-end (or hash n))
|
||||||
|
;; query
|
||||||
|
(qm (uri-index-of s #\? rest-start))
|
||||||
|
(query (and qm (< qm pre-frag-end) (substring s (+ qm 1) pre-frag-end)))
|
||||||
|
(hp-end (cond ((and qm (< qm pre-frag-end)) qm) (else pre-frag-end)))
|
||||||
|
;; authority (after "//")
|
||||||
|
(has-auth (and (<= (+ rest-start 2) n)
|
||||||
|
(char=? (string-ref s rest-start) #\/)
|
||||||
|
(char=? (string-ref s (+ rest-start 1)) #\/)))
|
||||||
|
(auth-start (and has-auth (+ rest-start 2)))
|
||||||
|
(auth-end (and has-auth
|
||||||
|
(let loop ((i auth-start))
|
||||||
|
(cond ((>= i hp-end) hp-end)
|
||||||
|
((char=? (string-ref s i) #\/) i)
|
||||||
|
(else (loop (+ i 1)))))))
|
||||||
|
(authority (and has-auth (substring s auth-start auth-end)))
|
||||||
|
(path-start (if has-auth auth-end rest-start))
|
||||||
|
(path (substring s path-start hp-end)))
|
||||||
|
;; host:port from authority (strip userinfo@)
|
||||||
|
(let* ((at (and authority (uri-index-of authority #\@ 0)))
|
||||||
|
(hostport (if at (substring authority (+ at 1) (string-length authority)) authority))
|
||||||
|
(colon (and hostport (uri-index-of hostport #\: 0)))
|
||||||
|
(host (cond ((not hostport) jolt-nil)
|
||||||
|
(colon (substring hostport 0 colon))
|
||||||
|
(else hostport)))
|
||||||
|
(port (if (and colon (< (+ colon 1) (string-length hostport)))
|
||||||
|
(or (string->number (substring hostport (+ colon 1) (string-length hostport))) -1)
|
||||||
|
-1)))
|
||||||
|
(make-jhost "uri"
|
||||||
|
(list (cons 'string s)
|
||||||
|
(cons 'scheme (or scheme jolt-nil))
|
||||||
|
(cons 'authority (or authority jolt-nil))
|
||||||
|
(cons 'host (if (and host (string? host) (= 0 (string-length host))) jolt-nil host))
|
||||||
|
(cons 'port (->num port))
|
||||||
|
(cons 'path (if (= 0 (string-length path)) (if has-auth "" jolt-nil) path))
|
||||||
|
(cons 'query (or query jolt-nil))
|
||||||
|
(cons 'fragment (or frag jolt-nil)))))))
|
||||||
|
(define (uri-field u k) (let ((p (assq k (jhost-state u)))) (if p (cdr p) jolt-nil)))
|
||||||
|
(register-class-ctor! "URI" (lambda (s) (uri-parse (jolt-str-render-one s))))
|
||||||
|
(register-class-ctor! "java.net.URI" (lambda (s) (uri-parse (jolt-str-render-one s))))
|
||||||
|
(register-host-methods! "uri"
|
||||||
|
(list (cons "toString" (lambda (u) (uri-field u 'string)))
|
||||||
|
(cons "toASCIIString" (lambda (u) (uri-field u 'string)))
|
||||||
|
(cons "getScheme" (lambda (u) (uri-field u 'scheme)))
|
||||||
|
(cons "getAuthority" (lambda (u) (uri-field u 'authority)))
|
||||||
|
(cons "getHost" (lambda (u) (uri-field u 'host)))
|
||||||
|
(cons "getPort" (lambda (u) (uri-field u 'port)))
|
||||||
|
(cons "getPath" (lambda (u) (uri-field u 'path)))
|
||||||
|
(cons "getRawPath" (lambda (u) (uri-field u 'path)))
|
||||||
|
(cons "getQuery" (lambda (u) (uri-field u 'query)))
|
||||||
|
(cons "getRawQuery" (lambda (u) (uri-field u 'query)))
|
||||||
|
(cons "getFragment" (lambda (u) (uri-field u 'fragment)))
|
||||||
|
(cons "isAbsolute" (lambda (u) (not (jolt-nil? (uri-field u 'scheme)))))
|
||||||
|
(cons "hashCode" (lambda (u) (string-hash (uri-field u 'string))))
|
||||||
|
(cons "equals" (lambda (u o) (and (jhost? o) (string=? (jhost-tag o) "uri")
|
||||||
|
(string=? (uri-field u 'string) (uri-field o 'string)))))))
|
||||||
|
;; str / pr-str of a uri -> its string form.
|
||||||
|
(define %uri-str-render-one jolt-str-render-one)
|
||||||
|
(set! jolt-str-render-one
|
||||||
|
(lambda (x) (if (and (jhost? x) (string=? (jhost-tag x) "uri")) (uri-field x 'string) (%uri-str-render-one x))))
|
||||||
|
(define %uri-pr-readable jolt-pr-readable)
|
||||||
|
(set! jolt-pr-readable
|
||||||
|
(lambda (x) (if (and (jhost? x) (string=? (jhost-tag x) "uri"))
|
||||||
|
(string-append "#object[java.net.URI \"" (uri-field x 'string) "\"]")
|
||||||
|
(%uri-pr-readable x))))
|
||||||
|
;; class of the host value types defined by now (uri/uuid/file).
|
||||||
|
(define %uri-class jolt-class)
|
||||||
|
(set! jolt-class
|
||||||
|
(lambda (x)
|
||||||
|
(cond ((and (jhost? x) (string=? (jhost-tag x) "uri")) "java.net.URI")
|
||||||
|
((juuid? x) "java.util.UUID")
|
||||||
|
((jfile? x) "java.io.File")
|
||||||
|
(else (%uri-class x)))))
|
||||||
|
(def-var! "clojure.core" "class" jolt-class)
|
||||||
|
|
|
||||||
73
host/chez/natives-queue.ss
Normal file
73
host/chez/natives-queue.ss
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
;; natives-queue.ss (jolt-b8he) — clojure.lang.PersistentQueue for the Chez host.
|
||||||
|
;;
|
||||||
|
;; A functional queue: a `front` Scheme list (the dequeue end, head = front of the
|
||||||
|
;; queue) + a reversed `rear` Scheme list (the enqueue end, head = most recent).
|
||||||
|
;; conj adds to rear; peek/first read front; pop drops the front, rebalancing
|
||||||
|
;; rear->front when front empties — amortized O(1). A queue is jolt-sequential?, so
|
||||||
|
;; seq=?/seq-hash give cross-type equality (= [1 2 3] (queue 1 2 3)) for free, like
|
||||||
|
;; the JVM. Loaded after seq/collections/lazy-bridge/records/host-table so every
|
||||||
|
;; dispatcher it chains is at its latest binding.
|
||||||
|
|
||||||
|
(define-record-type jolt-queue (fields front rear cnt) (nongenerative jolt-queue-v1))
|
||||||
|
(define jolt-queue-empty (make-jolt-queue '() '() 0))
|
||||||
|
|
||||||
|
(define (queue-conj q x)
|
||||||
|
(if (null? (jolt-queue-front q))
|
||||||
|
(make-jolt-queue (list x) '() (fx+ (jolt-queue-cnt q) 1))
|
||||||
|
(make-jolt-queue (jolt-queue-front q) (cons x (jolt-queue-rear q)) (fx+ (jolt-queue-cnt q) 1))))
|
||||||
|
(define (queue->list q) (append (jolt-queue-front q) (reverse (jolt-queue-rear q))))
|
||||||
|
(define (queue-peek q) (if (null? (jolt-queue-front q)) jolt-nil (car (jolt-queue-front q))))
|
||||||
|
(define (queue-pop q)
|
||||||
|
(let ((f (jolt-queue-front q)))
|
||||||
|
(cond ((null? f) (error 'pop "can't pop empty queue"))
|
||||||
|
((null? (cdr f)) (make-jolt-queue (reverse (jolt-queue-rear q)) '() (fx- (jolt-queue-cnt q) 1)))
|
||||||
|
(else (make-jolt-queue (cdr f) (jolt-queue-rear q) (fx- (jolt-queue-cnt q) 1))))))
|
||||||
|
|
||||||
|
;; --- extend the collection dispatchers to see a jolt-queue ------------------
|
||||||
|
(define %q-seq jolt-seq)
|
||||||
|
(set! jolt-seq (lambda (x) (if (jolt-queue? x)
|
||||||
|
(let ((l (queue->list x))) (if (null? l) jolt-nil (list->cseq l)))
|
||||||
|
(%q-seq x))))
|
||||||
|
(define %q-count jolt-count)
|
||||||
|
(set! jolt-count (lambda (x) (if (jolt-queue? x) (jolt-queue-cnt x) (%q-count x))))
|
||||||
|
(define %q-empty? jolt-empty?)
|
||||||
|
(set! jolt-empty? (lambda (x) (if (jolt-queue? x) (fx=? 0 (jolt-queue-cnt x)) (%q-empty? x))))
|
||||||
|
(define %q-peek jolt-peek)
|
||||||
|
(set! jolt-peek (lambda (x) (if (jolt-queue? x) (queue-peek x) (%q-peek x))))
|
||||||
|
(define %q-pop jolt-pop)
|
||||||
|
(set! jolt-pop (lambda (x) (if (jolt-queue? x) (queue-pop x) (%q-pop x))))
|
||||||
|
(define %q-conj1 jolt-conj1)
|
||||||
|
(set! jolt-conj1 (lambda (coll x) (if (jolt-queue? coll) (queue-conj coll x) (%q-conj1 coll x))))
|
||||||
|
;; sequential => seq=?/seq-hash handle queue equality + hashing.
|
||||||
|
(define %q-sequential? jolt-sequential?)
|
||||||
|
(set! jolt-sequential? (lambda (x) (or (jolt-queue? x) (%q-sequential? x))))
|
||||||
|
|
||||||
|
;; printing: render the elements as a parenthesized list (delegate to the seq path).
|
||||||
|
(define (jolt-seq-or-empty x) (let ((s (jolt-seq x))) (if (jolt-nil? s) jolt-empty-list s)))
|
||||||
|
(define %q-pr-readable jolt-pr-readable)
|
||||||
|
(set! jolt-pr-readable (lambda (x) (if (jolt-queue? x) (%q-pr-readable (jolt-seq-or-empty x)) (%q-pr-readable x))))
|
||||||
|
(define %q-str-render-one jolt-str-render-one)
|
||||||
|
(set! jolt-str-render-one (lambda (x) (if (jolt-queue? x) (%q-str-render-one (jolt-seq-or-empty x)) (%q-str-render-one x))))
|
||||||
|
|
||||||
|
;; class / type / instance? recognize a queue.
|
||||||
|
(define %q-class jolt-class)
|
||||||
|
(set! jolt-class (lambda (x) (if (jolt-queue? x) "clojure.lang.PersistentQueue" (%q-class x))))
|
||||||
|
(def-var! "clojure.core" "class" jolt-class)
|
||||||
|
(define %q-instance-check instance-check)
|
||||||
|
(set! instance-check
|
||||||
|
(lambda (type-sym val)
|
||||||
|
(if (jolt-queue? val)
|
||||||
|
(let ((tn (cond ((string? type-sym) type-sym)
|
||||||
|
((symbol-t? type-sym) (symbol-t-name type-sym)) (else ""))))
|
||||||
|
(and (member (last-dot tn)
|
||||||
|
'("PersistentQueue" "IPersistentCollection" "Sequential" "Collection" "Object"))
|
||||||
|
#t))
|
||||||
|
(%q-instance-check type-sym val))))
|
||||||
|
(def-var! "clojure.core" "instance-check" instance-check)
|
||||||
|
|
||||||
|
;; clojure.lang.PersistentQueue/EMPTY + a queue? predicate.
|
||||||
|
(register-class-statics! "PersistentQueue" (list (cons "EMPTY" jolt-queue-empty)))
|
||||||
|
(register-class-statics! "clojure.lang.PersistentQueue" (list (cons "EMPTY" jolt-queue-empty)))
|
||||||
|
(def-var! "clojure.core" "queue?" (lambda (x) (jolt-queue? x)))
|
||||||
|
;; the FQ class token self-evaluates (for (instance? clojure.lang.PersistentQueue …)).
|
||||||
|
(def-var! "clojure.core" "clojure.lang.PersistentQueue" "clojure.lang.PersistentQueue")
|
||||||
|
|
@ -84,6 +84,30 @@
|
||||||
(let loop ((p (reverse parts)))
|
(let loop ((p (reverse parts)))
|
||||||
(if (and (pair? p) (string=? (car p) "")) (loop (cdr p)) (reverse p))))
|
(if (and (pair? p) (string=? (car p) "")) (loop (cdr p)) (reverse p))))
|
||||||
|
|
||||||
|
;; Encode a string to bytes (a bytevector) under a named charset. UTF-8 default;
|
||||||
|
;; ISO-8859-1/latin1/ascii are one byte per char; UTF-16/UTF-32 via Chez's codecs
|
||||||
|
;; (plain "UTF-16" emits a big-endian BOM then BE, matching the JVM). Shared by
|
||||||
|
;; .getBytes and decode-bytevector (String.).
|
||||||
|
(define (charset-encode-bv s csname)
|
||||||
|
(let ((cs (ascii-string-down (if (string? csname) csname (jolt-str-render-one csname)))))
|
||||||
|
(cond
|
||||||
|
((or (string=? cs "utf-8") (string=? cs "utf8")) (string->utf8 s))
|
||||||
|
((member cs '("iso-8859-1" "latin1" "iso8859-1" "us-ascii" "ascii"))
|
||||||
|
(let* ((n (string-length s)) (bv (make-bytevector n)))
|
||||||
|
(do ((i 0 (+ i 1))) ((= i n) bv)
|
||||||
|
(bytevector-u8-set! bv i (bitwise-and (char->integer (string-ref s i)) #xff)))))
|
||||||
|
((string=? cs "utf-16be") (string->utf16 s (endianness big)))
|
||||||
|
((string=? cs "utf-16le") (string->utf16 s (endianness little)))
|
||||||
|
((or (string=? cs "utf-16") (string=? cs "utf16") (string=? cs "unicode"))
|
||||||
|
(let ((be (string->utf16 s (endianness big))))
|
||||||
|
(let* ((n (bytevector-length be)) (bv (make-bytevector (+ n 2))))
|
||||||
|
(bytevector-u8-set! bv 0 #xfe) (bytevector-u8-set! bv 1 #xff)
|
||||||
|
(bytevector-copy! be 0 bv 2 n) bv)))
|
||||||
|
((or (string=? cs "utf-32be") (string=? cs "utf-32") (string=? cs "utf32"))
|
||||||
|
(string->utf32 s (endianness big)))
|
||||||
|
((string=? cs "utf-32le") (string->utf32 s (endianness little)))
|
||||||
|
(else (string->utf8 s)))))
|
||||||
|
|
||||||
(define (jolt-string-method method s rest)
|
(define (jolt-string-method method s rest)
|
||||||
(define (arg n) (list-ref rest n))
|
(define (arg n) (list-ref rest n))
|
||||||
(cond
|
(cond
|
||||||
|
|
@ -118,16 +142,12 @@
|
||||||
((string=? method "compareTo")
|
((string=? method "compareTo")
|
||||||
(let ((o (arg 0))) (cond ((string<? s o) -1.0) ((string>? s o) 1.0) (else 0.0))))
|
(let ((o (arg 0))) (cond ((string<? s o) -1.0) ((string>? s o) 1.0) (else 0.0))))
|
||||||
((string=? method "getBytes")
|
((string=? method "getBytes")
|
||||||
;; (.getBytes s) / (.getBytes s charset). UTF-8 default; ISO-8859-1/latin1/
|
;; (.getBytes s) / (.getBytes s charset) -> a jolt byte-array (seqable /
|
||||||
;; ascii encode one byte per char (clj-http-lite's body-encoding path).
|
;; countable / alength-able, like (byte-array …)); the JVM returns byte[].
|
||||||
(if (null? rest)
|
(na-byte-array
|
||||||
(string->utf8 s)
|
(charset-encode-bv s (if (null? rest)
|
||||||
(let ((cn (ascii-string-down (if (string? (arg 0)) (arg 0) (jolt-str-render-one (arg 0))))))
|
"utf-8"
|
||||||
(if (member cn '("iso-8859-1" "latin1" "iso8859-1" "us-ascii" "ascii"))
|
(if (string? (arg 0)) (arg 0) (jolt-str-render-one (arg 0)))))))
|
||||||
(let* ((n (string-length s)) (bv (make-bytevector n)))
|
|
||||||
(do ((i 0 (+ i 1))) ((= i n) bv)
|
|
||||||
(bytevector-u8-set! bv i (bitwise-and (char->integer (string-ref s i)) #xff))))
|
|
||||||
(string->utf8 s)))))
|
|
||||||
((string=? method "matches") (if (irregex-match (str-irx (arg 0)) s) #t #f))
|
((string=? method "matches") (if (irregex-match (str-irx (arg 0)) s) #t #f))
|
||||||
((string=? method "replaceAll") (irregex-replace/all (str-irx (arg 0)) s (arg 1)))
|
((string=? method "replaceAll") (irregex-replace/all (str-irx (arg 0)) s (arg 1)))
|
||||||
((string=? method "replaceFirst") (irregex-replace (str-irx (arg 0)) s (arg 1)))
|
((string=? method "replaceFirst") (irregex-replace (str-irx (arg 0)) s (arg 1)))
|
||||||
|
|
@ -141,6 +161,11 @@
|
||||||
;; String.intern: jolt strings aren't pooled, but value equality holds, so the
|
;; String.intern: jolt strings aren't pooled, but value equality holds, so the
|
||||||
;; canonical representation is the string itself.
|
;; canonical representation is the string itself.
|
||||||
((string=? method "intern") s)
|
((string=? method "intern") s)
|
||||||
|
;; A class token is its canonical-name string, so Class methods land here:
|
||||||
|
;; (.getName (.getClass x)) / (.getSimpleName …) over the name string.
|
||||||
|
((or (string=? method "getName") (string=? method "getCanonicalName")) s)
|
||||||
|
((string=? method "getSimpleName")
|
||||||
|
(let ((i (str-last-index-of s "."))) (if (>= i 0) (substring s (+ i 1) (string-length s)) s)))
|
||||||
(else (error #f (string-append "No method " method " for value")))))
|
(else (error #f (string-append "No method " method " for value")))))
|
||||||
|
|
||||||
;; --- clojure.core str-* primitives (the substrate clojure.string.clj calls) ---
|
;; --- clojure.core str-* primitives (the substrate clojure.string.clj calls) ---
|
||||||
|
|
|
||||||
|
|
@ -97,3 +97,7 @@
|
||||||
;; chunked-seq? is true for a vector's seq (a real chunked-seq); the overlay's
|
;; chunked-seq? is true for a vector's seq (a real chunked-seq); the overlay's
|
||||||
;; always-false stub loaded over the host fn, so re-assert it (jolt-hs5q).
|
;; always-false stub loaded over the host fn, so re-assert it (jolt-hs5q).
|
||||||
(def-var! "clojure.core" "chunked-seq?" na-chunked-seq?)
|
(def-var! "clojure.core" "chunked-seq?" na-chunked-seq?)
|
||||||
|
;; record? is a host type check (jrec?), not the overlay's (some? (get x
|
||||||
|
;; :jolt/deftype)) — the get-trick invokes a sorted-map's comparator on
|
||||||
|
;; :jolt/deftype and throws (jolt-3bbj). Matches the JVM (instance? IRecord).
|
||||||
|
(def-var! "clojure.core" "record?" (lambda (x) (jrec? x)))
|
||||||
|
|
|
||||||
|
|
@ -139,12 +139,16 @@
|
||||||
((boolean? obj) '("Boolean" "Object"))
|
((boolean? obj) '("Boolean" "Object"))
|
||||||
((keyword? obj) '("Keyword" "Named" "Object"))
|
((keyword? obj) '("Keyword" "Named" "Object"))
|
||||||
((jolt-symbol? obj) '("Symbol" "Named" "Object"))
|
((jolt-symbol? obj) '("Symbol" "Named" "Object"))
|
||||||
((pvec? obj) '("PersistentVector" "IPersistentVector" "IPersistentCollection"
|
((pvec? obj) '("PersistentVector" "APersistentVector" "IPersistentVector" "IPersistentCollection"
|
||||||
"List" "java.util.List" "Sequential" "Collection" "Object"))
|
"List" "java.util.List" "Sequential" "Collection" "Object"))
|
||||||
((pmap? obj) '("PersistentArrayMap" "IPersistentMap" "Associative"
|
((pmap? obj) '("PersistentArrayMap" "APersistentMap" "IPersistentMap" "Associative"
|
||||||
"Map" "java.util.Map" "Object"))
|
"Map" "java.util.Map" "Object"))
|
||||||
((pset? obj) '("PersistentHashSet" "IPersistentSet" "Set" "java.util.Set" "Collection" "Object"))
|
((pset? obj) '("PersistentHashSet" "APersistentSet" "IPersistentSet" "Set" "java.util.Set" "Collection" "Object"))
|
||||||
((or (cseq? obj) (empty-list-t? obj)) '("ISeq" "IPersistentCollection" "Sequential" "Collection" "Object"))
|
((or (cseq? obj) (empty-list-t? obj)) '("ASeq" "ISeq" "IPersistentCollection" "Sequential" "Collection" "Object"))
|
||||||
|
;; java.net.URI jhost — extend-protocol java.net.URI (hiccup ToURI/ToStr).
|
||||||
|
((and (jhost? obj) (string=? (jhost-tag obj) "uri")) '("URI" "java.net.URI" "Object"))
|
||||||
|
;; a bare procedure (fn) — extend-protocol to clojure.lang.{Fn,IFn,AFn}.
|
||||||
|
((procedure? obj) '("Fn" "IFn" "AFn" "Object"))
|
||||||
((jolt-nil? obj) '("nil"))
|
((jolt-nil? obj) '("nil"))
|
||||||
(else '("Object"))))
|
(else '("Object"))))
|
||||||
|
|
||||||
|
|
@ -185,9 +189,11 @@
|
||||||
'("Long" "Integer" "Number" "Double" "Ratio" "BigInt" "BigInteger"
|
'("Long" "Integer" "Number" "Double" "Ratio" "BigInt" "BigInteger"
|
||||||
"String" "CharSequence" "Boolean" "Character"
|
"String" "CharSequence" "Boolean" "Character"
|
||||||
"Keyword" "Symbol" "Named" "Object" "nil"
|
"Keyword" "Symbol" "Named" "Object" "nil"
|
||||||
"PersistentVector" "IPersistentVector"
|
"Fn" "IFn" "AFn" "URI"
|
||||||
"PersistentArrayMap" "IPersistentMap" "PersistentHashSet" "IPersistentSet"
|
"PersistentVector" "APersistentVector" "IPersistentVector"
|
||||||
"ISeq" "IPersistentCollection" "Associative" "Sequential"
|
"PersistentArrayMap" "APersistentMap" "IPersistentMap"
|
||||||
|
"PersistentHashSet" "APersistentSet" "IPersistentSet"
|
||||||
|
"ASeq" "ISeq" "IPersistentCollection" "Associative" "Sequential"
|
||||||
"Map" "java.util.Map" "List" "java.util.List" "Set" "java.util.Set"
|
"Map" "java.util.Map" "List" "java.util.List" "Set" "java.util.Set"
|
||||||
"Collection" "java.util.Collection"))
|
"Collection" "java.util.Collection"))
|
||||||
h))
|
h))
|
||||||
|
|
@ -197,6 +203,7 @@
|
||||||
(define (canonical-host-tag type-name)
|
(define (canonical-host-tag type-name)
|
||||||
(let ((base (or (strip-prefix type-name "java.lang.")
|
(let ((base (or (strip-prefix type-name "java.lang.")
|
||||||
(strip-prefix type-name "java.util.")
|
(strip-prefix type-name "java.util.")
|
||||||
|
(strip-prefix type-name "java.net.")
|
||||||
(strip-prefix type-name "clojure.lang.")
|
(strip-prefix type-name "clojure.lang.")
|
||||||
type-name)))
|
type-name)))
|
||||||
(and (hashtable-ref host-type-set base #f) base)))
|
(and (hashtable-ref host-type-set base #f) base)))
|
||||||
|
|
@ -264,6 +271,11 @@
|
||||||
(define (record-method-dispatch obj method-name rest-args)
|
(define (record-method-dispatch obj method-name rest-args)
|
||||||
(let ((rest (if (jolt-nil? rest-args) '() (seq->list rest-args))))
|
(let ((rest (if (jolt-nil? rest-args) '() (seq->list rest-args))))
|
||||||
(cond
|
(cond
|
||||||
|
;; (.getClass x): universal Object method — the class token for any value
|
||||||
|
;; (jolt has no Class objects; the token is the canonical name string, on
|
||||||
|
;; which .getName/.getSimpleName work via the String method shim).
|
||||||
|
((and (string=? method-name "getClass") (not (jrec? obj)) (not (jreify? obj)))
|
||||||
|
(jolt-class obj))
|
||||||
((and (jrec? obj) (find-method-any-protocol (jrec-tag obj) method-name))
|
((and (jrec? obj) (find-method-any-protocol (jrec-tag obj) method-name))
|
||||||
=> (lambda (f) (apply jolt-invoke f obj rest)))
|
=> (lambda (f) (apply jolt-invoke f obj rest)))
|
||||||
((reified-methods obj)
|
((reified-methods obj)
|
||||||
|
|
@ -321,6 +333,19 @@
|
||||||
((string=? method-name "compareTo")
|
((string=? method-name "compareTo")
|
||||||
(let ((o (car rest))) (cond ((char<? obj o) -1) ((char>? obj o) 1) (else 0))))
|
(let ((o (car rest))) (cond ((char<? obj o) -1) ((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 " on char")))))
|
||||||
|
;; java.util.List .indexOf / .lastIndexOf over any seqable (vector / list /
|
||||||
|
;; seq) — -1 when absent, like the JVM (medley/index-of reads this).
|
||||||
|
((or (string=? method-name "indexOf") (string=? method-name "lastIndexOf"))
|
||||||
|
(let ((target (car rest)) (last? (string=? method-name "lastIndexOf")))
|
||||||
|
(let loop ((s (jolt-seq obj)) (i 0) (found -1))
|
||||||
|
(cond ((jolt-nil? s) found)
|
||||||
|
((jolt=2 (seq-first s) target)
|
||||||
|
(if last? (loop (jolt-seq (seq-more s)) (fx+ i 1) i) i))
|
||||||
|
(else (loop (jolt-seq (seq-more s)) (fx+ i 1) found))))))
|
||||||
|
;; universal Object methods on any remaining value (boolean, etc.).
|
||||||
|
((string=? method-name "toString") (jolt-str-render-one obj))
|
||||||
|
((string=? method-name "hashCode") (jolt-hash obj))
|
||||||
|
((string=? method-name "equals") (and (pair? rest) (if (jolt= obj (car rest)) #t #f)))
|
||||||
(else (error #f (string-append "No method " method-name " for value: "
|
(else (error #f (string-append "No method " method-name " for value: "
|
||||||
(jolt-pr-str obj)))))))
|
(jolt-pr-str obj)))))))
|
||||||
|
|
||||||
|
|
@ -371,6 +396,55 @@
|
||||||
(hashtable-keys type-registry))
|
(hashtable-keys type-registry))
|
||||||
(if (null? out) jolt-nil (list->cseq out))))
|
(if (null? out) jolt-nil (list->cseq out))))
|
||||||
|
|
||||||
|
;; jolt exception values (ex-info + host-constructed throwables) are ex-info-shaped
|
||||||
|
;; maps tagged :jolt/type :jolt/ex-info; (class …)/instance? read the JVM class off
|
||||||
|
;; the optional :jolt/class key, defaulting to clojure.lang.ExceptionInfo.
|
||||||
|
;; pmap? guard: ex-info maps are plain hash-maps, never sorted-map htables — and a
|
||||||
|
;; bare jolt-get on a sorted-map would invoke its comparator on :jolt/type and throw.
|
||||||
|
(define (ex-info-map? v)
|
||||||
|
(and (pmap? v) (jolt=2 (jolt-get v jolt-kw-ex-type jolt-nil) jolt-kw-ex-info)))
|
||||||
|
(define (ex-info-class v)
|
||||||
|
(let ((c (jolt-get v jolt-kw-class jolt-nil)))
|
||||||
|
(if (string? c) c "clojure.lang.ExceptionInfo")))
|
||||||
|
;; immediate-parent chain of the JVM exception hierarchy (simple names). Drives
|
||||||
|
;; instance? across exception supertypes — (instance? Throwable (ex-info …)) etc.
|
||||||
|
(define exception-parent
|
||||||
|
'(("ExceptionInfo" . "RuntimeException")
|
||||||
|
("RuntimeException" . "Exception")
|
||||||
|
("IllegalArgumentException" . "RuntimeException")
|
||||||
|
("NumberFormatException" . "IllegalArgumentException")
|
||||||
|
("IllegalStateException" . "RuntimeException")
|
||||||
|
("UnsupportedOperationException" . "RuntimeException")
|
||||||
|
("ArithmeticException" . "RuntimeException")
|
||||||
|
("NullPointerException" . "RuntimeException")
|
||||||
|
("ClassCastException" . "RuntimeException")
|
||||||
|
("IndexOutOfBoundsException" . "RuntimeException")
|
||||||
|
("ConcurrentModificationException" . "RuntimeException")
|
||||||
|
("NoSuchElementException" . "RuntimeException")
|
||||||
|
("UncheckedIOException" . "RuntimeException")
|
||||||
|
("InterruptedException" . "Exception")
|
||||||
|
("IOException" . "Exception")
|
||||||
|
("FileNotFoundException" . "IOException")
|
||||||
|
("UnsupportedEncodingException" . "IOException")
|
||||||
|
("UnknownHostException" . "IOException")
|
||||||
|
("SocketException" . "IOException")
|
||||||
|
("ConnectException" . "IOException")
|
||||||
|
("SocketTimeoutException" . "IOException")
|
||||||
|
("MalformedURLException" . "IOException")
|
||||||
|
("SSLException" . "IOException")
|
||||||
|
("Exception" . "Throwable")
|
||||||
|
("Error" . "Throwable")
|
||||||
|
("AssertionError" . "Error")
|
||||||
|
("Throwable" . "Object")))
|
||||||
|
;; Is `wanted` (simple name) `cls` or a supertype of it? ExceptionInfo also
|
||||||
|
;; implements the IExceptionInfo interface.
|
||||||
|
(define (exception-isa? cls wanted)
|
||||||
|
(let loop ((c cls))
|
||||||
|
(cond ((not c) #f)
|
||||||
|
((string=? c wanted) #t)
|
||||||
|
((and (string=? c "ExceptionInfo") (string=? wanted "IExceptionInfo")) #t)
|
||||||
|
(else (let ((p (assoc c exception-parent))) (loop (and p (cdr p))))))))
|
||||||
|
|
||||||
;; instance-check: (type-sym val) — type/protocol membership.
|
;; instance-check: (type-sym val) — type/protocol membership.
|
||||||
(define (instance-check type-sym val)
|
(define (instance-check type-sym val)
|
||||||
(let ((tname (symbol-t-name type-sym)))
|
(let ((tname (symbol-t-name type-sym)))
|
||||||
|
|
@ -382,6 +456,7 @@
|
||||||
(string=? (substring tag (- (string-length tag) (string-length tname)) (string-length tag)) tname)))))
|
(string=? (substring tag (- (string-length tag) (string-length tname)) (string-length tag)) tname)))))
|
||||||
((jreify? val) (let ((short (last-dot tname)))
|
((jreify? val) (let ((short (last-dot tname)))
|
||||||
(and (memp (lambda (p) (string=? (last-dot p) short)) (jreify-protos val)) #t)))
|
(and (memp (lambda (p) (string=? (last-dot p) short)) (jreify-protos val)) #t)))
|
||||||
|
((ex-info-map? val) (exception-isa? (last-dot (ex-info-class val)) (last-dot tname)))
|
||||||
(else (case-string tname val)))))
|
(else (case-string tname val)))))
|
||||||
(define (case-string tname val)
|
(define (case-string tname val)
|
||||||
(cond
|
(cond
|
||||||
|
|
@ -397,6 +472,11 @@
|
||||||
((member tname '("Symbol" "clojure.lang.Symbol")) (jolt-symbol? val))
|
((member tname '("Symbol" "clojure.lang.Symbol")) (jolt-symbol? val))
|
||||||
((member tname '("Atom" "clojure.lang.Atom")) (jolt-atom? val))
|
((member tname '("Atom" "clojure.lang.Atom")) (jolt-atom? val))
|
||||||
((member tname '("IFn" "clojure.lang.IFn" "Fn" "clojure.lang.Fn")) (procedure? val))
|
((member tname '("IFn" "clojure.lang.IFn" "Fn" "clojure.lang.Fn")) (procedure? val))
|
||||||
|
((member tname '("Pattern" "java.util.regex.Pattern")) (regex-t? val))
|
||||||
|
((member tname '("URI" "java.net.URI"))
|
||||||
|
(and (jhost? val) (string=? (jhost-tag val) "uri")))
|
||||||
|
((member tname '("File" "java.io.File")) (jfile? val))
|
||||||
|
((member tname '("UUID" "java.util.UUID")) (juuid? val))
|
||||||
(else #f)))
|
(else #f)))
|
||||||
|
|
||||||
;; str of a record uses a custom (Object toString) impl if the type defines one
|
;; str of a record uses a custom (Object toString) impl if the type defines one
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,17 @@
|
||||||
jolt-kw-message msg
|
jolt-kw-message msg
|
||||||
jolt-kw-data data
|
jolt-kw-data data
|
||||||
jolt-kw-cause (if (null? more) jolt-nil (car more))))
|
jolt-kw-cause (if (null? more) jolt-nil (car more))))
|
||||||
|
;; A host-constructed throwable (RuntimeException. etc.): an ex-info-shaped map
|
||||||
|
;; carrying its canonical JVM :jolt/class, so (class …) / instance? / .getMessage /
|
||||||
|
;; ex-message all reflect the real type. Plain ex-info has no :jolt/class (its class
|
||||||
|
;; defaults to clojure.lang.ExceptionInfo), so those maps stay byte-identical.
|
||||||
|
(define jolt-kw-class (keyword "jolt" "class"))
|
||||||
|
(define (jolt-host-throwable class-name msg . more)
|
||||||
|
(jolt-hash-map jolt-kw-ex-type jolt-kw-ex-info
|
||||||
|
jolt-kw-class class-name
|
||||||
|
jolt-kw-message msg
|
||||||
|
jolt-kw-data jolt-nil
|
||||||
|
jolt-kw-cause (if (null? more) jolt-nil (car more))))
|
||||||
|
|
||||||
;; --- host interop (jolt-0kf5) ------------------------------------------------
|
;; --- host interop (jolt-0kf5) ------------------------------------------------
|
||||||
;; (.method target arg*) lowers to (jolt-host-call "method" target arg*). JVM
|
;; (.method target arg*) lowers to (jolt-host-call "method" target arg*). JVM
|
||||||
|
|
@ -331,6 +342,11 @@
|
||||||
;; it. After the dispatchers it chains.
|
;; it. After the dispatchers it chains.
|
||||||
(load "host/chez/natives-array.ss")
|
(load "host/chez/natives-array.ss")
|
||||||
|
|
||||||
|
;; clojure.lang.PersistentQueue (jolt-b8he): a functional queue + EMPTY static.
|
||||||
|
;; Chains seq/count/empty?/peek/pop/conj/sequential?/class/instance?/printer, so
|
||||||
|
;; load after natives-array (the dispatchers it extends).
|
||||||
|
(load "host/chez/natives-queue.ss")
|
||||||
|
|
||||||
;; syntax-quote form builders (jolt-r9lm, inc6b): __sqcat/__sqvec/__sqmap/__sqset/
|
;; syntax-quote form builders (jolt-r9lm, inc6b): __sqcat/__sqvec/__sqmap/__sqset/
|
||||||
;; __sq1, def-var!'d into clojure.core. A cross-compiled macro expander (analyzer
|
;; __sq1, def-var!'d into clojure.core. A cross-compiled macro expander (analyzer
|
||||||
;; on Chez, inc6b) calls these to build its expansion as reader forms. Needs the
|
;; on Chez, inc6b) calls these to build its expansion as reader forms. Needs the
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -241,10 +241,12 @@
|
||||||
env* (add-locals env names)
|
env* (add-locals env names)
|
||||||
binds (mapv (fn [spec]
|
binds (mapv (fn [spec]
|
||||||
(let [cl (vec (form-elements spec))]
|
(let [cl (vec (form-elements spec))]
|
||||||
;; analyze as a named fn (items[1] = the name): self- and
|
;; Build (fn name [params] body*) and analyze through the fn
|
||||||
;; sibling-calls resolve, the fn carries its own name.
|
;; MACRO so destructuring params desugar (the fn* primitive
|
||||||
|
;; would not — same trick defmacro uses). The named fn means
|
||||||
|
;; self- and sibling-calls resolve and it carries its own name.
|
||||||
[(form-sym-name (first cl))
|
[(form-sym-name (first cl))
|
||||||
(analyze-fn ctx (vec (cons (first cl) cl)) env*)]))
|
(analyze ctx (cons (symbol "fn") cl) env*)]))
|
||||||
specs)]
|
specs)]
|
||||||
{:op :let :letrec true :bindings binds
|
{:op :let :letrec true :bindings binds
|
||||||
:body (analyze-seq ctx (drop 2 items) env*)}))
|
:body (analyze-seq ctx (drop 2 items) env*)}))
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,56 @@
|
||||||
{:suite "dotform" :expr "(try (throw \"boom\") (catch Throwable e (.getMessage e)))" :expected "boom"}
|
{:suite "dotform" :expr "(try (throw \"boom\") (catch Throwable e (.getMessage e)))" :expected "boom"}
|
||||||
{:suite "dotform" :expr "(try (throw (Exception. \"boom\")) (catch Throwable e (.getMessage e)))" :expected "boom"}
|
{:suite "dotform" :expr "(try (throw (Exception. \"boom\")) (catch Throwable e (.getMessage e)))" :expected "boom"}
|
||||||
{:suite "dotform" :expr "(try (throw (IllegalArgumentException. \"bad\")) (catch Exception e (.getMessage e)))" :expected "bad"}
|
{:suite "dotform" :expr "(try (throw (IllegalArgumentException. \"bad\")) (catch Exception e (.getMessage e)))" :expected "bad"}
|
||||||
|
{:suite "exinfo" :expr "(instance? clojure.lang.ExceptionInfo (ex-info \"x\" {}))" :expected "true"}
|
||||||
|
{:suite "exinfo" :expr "(instance? clojure.lang.IExceptionInfo (ex-info \"x\" {}))" :expected "true"}
|
||||||
|
{:suite "exinfo" :expr "(instance? RuntimeException (ex-info \"x\" {}))" :expected "true"}
|
||||||
|
{:suite "exinfo" :expr "(instance? Throwable (ex-info \"x\" {}))" :expected "true"}
|
||||||
|
{:suite "exinfo" :expr "(class (ex-info \"x\" {}))" :expected "clojure.lang.ExceptionInfo"}
|
||||||
|
{:suite "exinfo" :expr "(= clojure.lang.ExceptionInfo (class (ex-info \"x\" {})))" :expected "true"}
|
||||||
|
{:suite "exinfo" :expr "(class (RuntimeException. \"x\"))" :expected "java.lang.RuntimeException"}
|
||||||
|
{:suite "exinfo" :expr "(instance? RuntimeException (RuntimeException. \"x\"))" :expected "true"}
|
||||||
|
{:suite "exinfo" :expr "(instance? Exception (IllegalArgumentException. \"x\"))" :expected "true"}
|
||||||
|
{:suite "exinfo" :expr "(instance? Throwable (IllegalArgumentException. \"x\"))" :expected "true"}
|
||||||
|
{:suite "exinfo" :expr "(instance? IllegalArgumentException (RuntimeException. \"x\"))" :expected "false"}
|
||||||
|
{:suite "exinfo" :expr "(instance? RuntimeException (InterruptedException. \"x\"))" :expected "false"}
|
||||||
|
{:suite "exinfo" :expr "(.getMessage (RuntimeException. \"boom\"))" :expected "boom"}
|
||||||
|
{:suite "exinfo" :expr "(ex-message (RuntimeException. \"boom\"))" :expected "boom"}
|
||||||
|
{:suite "exinfo" :expr "(type (ex-info \"x\" {}))" :expected ":jolt/ex-info"}
|
||||||
|
{:suite "hostobj" :expr "(.getName (.getClass \"x\"))" :expected "java.lang.String"}
|
||||||
|
{:suite "hostobj" :expr "(.getClass 5)" :expected "java.lang.Long"}
|
||||||
|
{:suite "hostobj" :expr "(.getSimpleName (.getClass :k))" :expected "Keyword"}
|
||||||
|
{:suite "hostobj" :expr "(.toString true)" :expected "true"}
|
||||||
|
{:suite "hostobj" :expr "(class (ex-info \"x\" {}))" :expected "clojure.lang.ExceptionInfo"}
|
||||||
|
{:suite "hostobj" :expr "[(instance? Double 1.5) (Double. \"3.5\") (Double/parseDouble \"2.5\")]" :expected "[true 3.5 2.5]"}
|
||||||
|
{:suite "hostobj" :expr "(instance? java.util.regex.Pattern #\"x\")" :expected "true"}
|
||||||
|
{:suite "hostobj" :expr "(class #\"x\")" :expected "java.util.regex.Pattern"}
|
||||||
|
{:suite "hostobj" :expr "[(namespace (symbol \"foo/bar\")) (name (symbol \"foo/bar\"))]" :expected "[foo bar]"}
|
||||||
|
{:suite "hostobj" :expr "(str (symbol \"a/b/c\"))" :expected "a/b/c"}
|
||||||
|
{:suite "hostobj" :expr "(do (System/setProperty \"jolt.test.x\" \"z\") (System/getProperty \"jolt.test.x\"))" :expected "z"}
|
||||||
|
{:suite "hostobj" :expr "(set! *assert* false)" :expected "false"}
|
||||||
|
{:suite "hostobj" :expr "(binding [*print-readably* false] *print-readably*)" :expected "false"}
|
||||||
|
{:suite "hostobj" :expr "(do (defprotocol P (m [_])) (extend-protocol P clojure.lang.Fn (m [_] :fn)) (m (fn [x] x)))" :expected ":fn"}
|
||||||
|
{:suite "hostobj" :expr "(do (defprotocol P (m [_])) (extend-protocol P clojure.lang.APersistentVector (m [_] :vec)) (m [1 2 3]))" :expected ":vec"}
|
||||||
|
{:suite "hostobj" :expr "(record? (sorted-map 1 2 3 4))" :expected "false"}
|
||||||
|
{:suite "queue" :expr "(seq (conj clojure.lang.PersistentQueue/EMPTY 1 2 3))" :expected "(1 2 3)"}
|
||||||
|
{:suite "queue" :expr "(peek (conj clojure.lang.PersistentQueue/EMPTY 1 2 3))" :expected "1"}
|
||||||
|
{:suite "queue" :expr "(seq (pop (conj clojure.lang.PersistentQueue/EMPTY 1 2 3)))" :expected "(2 3)"}
|
||||||
|
{:suite "queue" :expr "(count (conj clojure.lang.PersistentQueue/EMPTY 1 2 3))" :expected "3"}
|
||||||
|
{:suite "queue" :expr "(empty? clojure.lang.PersistentQueue/EMPTY)" :expected "true"}
|
||||||
|
{:suite "queue" :expr "(instance? clojure.lang.PersistentQueue (conj clojure.lang.PersistentQueue/EMPTY 1))" :expected "true"}
|
||||||
|
{:suite "queue" :expr "(= [1 2 3] (into clojure.lang.PersistentQueue/EMPTY [1 2 3]))" :expected "true"}
|
||||||
|
{:suite "queue" :expr "(first (into clojure.lang.PersistentQueue/EMPTY [1 2 3]))" :expected "1"}
|
||||||
|
{:suite "destructure" :expr "(letfn [(up [s [k & ks]] (cons s ks))] (up 1 [2 3 4]))" :expected "(1 3 4)"}
|
||||||
|
{:suite "hostctor" :expr "(.getName (java.io.File. \"/tmp/abc.txt\"))" :expected "abc.txt"}
|
||||||
|
{:suite "hostctor" :expr "(instance? java.io.File (java.io.File. \"/x\"))" :expected "true"}
|
||||||
|
{:suite "hostctor" :expr "(uuid? (java.util.UUID/randomUUID))" :expected "true"}
|
||||||
|
{:suite "hostctor" :expr "(instance? java.util.UUID (java.util.UUID/fromString \"12345678-1234-1234-1234-123456789abc\"))" :expected "true"}
|
||||||
|
{:suite "hostctor" :expr "(let [u (java.net.URI. \"http://h.com:81/p?q=1#f\")] [(.getScheme u) (.getHost u) (.getPort u) (.getPath u) (.getQuery u) (.getFragment u)])" :expected "[http h.com 81 /p q=1 f]"}
|
||||||
|
{:suite "hostctor" :expr "(nil? (.getHost (java.net.URI. \"/rel/path\")))" :expected "true"}
|
||||||
|
{:suite "hostctor" :expr "(str (java.net.URI. \"http://x/y\"))" :expected "http://x/y"}
|
||||||
|
{:suite "hostctor" :expr "(instance? java.net.URI (java.net.URI. \"/x\"))" :expected "true"}
|
||||||
|
{:suite "hostctor" :expr "(let [a (java.util.ArrayList.)] (.add a 1) (.add a 2) (.add a 3) (.remove a 0) [(.size a) (vec (.toArray a))])" :expected "[2 [2 3]]"}
|
||||||
|
{:suite "hostctor" :expr "[(.indexOf [:a :b :c] :b) (.indexOf (list :a :b) :b) (.indexOf [:a] :z) (.indexOf (range 0 10) 1)]" :expected "[1 1 -1 1]"}
|
||||||
{:suite "dotform" :expr "(.equals \"a\" \"a\")" :expected "true"}
|
{:suite "dotform" :expr "(.equals \"a\" \"a\")" :expected "true"}
|
||||||
{:suite "dotform" :expr "(.equals \"a\" \"b\")" :expected "false"}
|
{:suite "dotform" :expr "(.equals \"a\" \"b\")" :expected "false"}
|
||||||
{:suite "dotform" :expr "(. {:value 41 :describe (fn [self] (str \"v=\" (:value self)))} describe)" :expected "v=41"}
|
{:suite "dotform" :expr "(. {:value 41 :describe (fn [self] (str \"v=\" (:value self)))} describe)" :expected "v=41"}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue