General fixes shaken out by clojure/tools.reader
Running clojure.tools.reader's own suite on jolt surfaced a batch of general gaps (all runtime, JVM-certified, no re-mint — reader.ss is loaded at runtime and jolt-core has no octal literals, so selfhost holds): Reader: - (load "rel") resolves a non-/ path against the current namespace's directory, like Clojure — (load "common_tests") from clojure.tools.reader-test loads clojure/tools/common_tests.clj. Was resolved against the roots directly. - Octal integer literals: 042 reads as 34, not decimal 42; octal string escapes (\377 is one char, not \0 + "00"). \oNNN char octal already worked. - (symbol nil name) now equals (symbol name) and the reader literal — a nil namespace is the #f no-ns sentinel, not jolt-nil (jolt= compares ns by equal?). clojure.test: - thrown-with-msg? honors the class hierarchy (instance?) before falling back to a simple-name match, so (thrown-with-msg? RuntimeException ...) matches an ExceptionInfo, like thrown? already did. Host interop (java layer): - java.util.regex: Pattern.matcher / Matcher.matches / .group / .groupCount / .find, and Pattern/compile. - clojure.lang: RT/map, PersistentList/create, PersistentHashSet/createWithCheck. - java.lang.Character: digit / isDigit / isWhitespace / valueOf. - java.util.LinkedList (Deque surface over the ArrayList backing); ArrayList / LinkedList are now seqable. - BigInteger 2-arg ctor (string, radix) + .negate / .bitLength / .signum / .abs; BigInt/fromBigInteger and Numbers/reduceBigInt (identity on jolt's exact ints). Suite: reader_test 22/30, reader-edn_test 13/16. The remaining failures are fundamental numeric-model differences (no BigDecimal type; BigInt and Long are one exact-integer type) or need JVM reflection (record/ctor tagged literals via getConstructors) — out of scope. make test green (+8 corpus rows, 0 new divergences), shakesmoke byte-identical.
This commit is contained in:
parent
850a84c272
commit
e16085402b
9 changed files with 217 additions and 21 deletions
|
|
@ -133,7 +133,12 @@
|
|||
(else (loop (- i 1))))))))
|
||||
((keyword? a) (jolt-symbol (keyword-t-ns a) (keyword-t-name a)))
|
||||
(else (error #f "symbol: requires string/symbol" a)))))
|
||||
((= (length args) 2) (jolt-symbol (car args) (cadr args)))
|
||||
;; (symbol ns name): a nil namespace is the no-ns sentinel #f (NOT jolt-nil),
|
||||
;; so (symbol nil "x") equals (symbol "x") and the reader literal 'x — jolt=
|
||||
;; compares ns with strict equal?, so a jolt-nil ns would differ from #f.
|
||||
((= (length args) 2)
|
||||
(let ((ns (car args)))
|
||||
(jolt-symbol (if (jolt-nil? ns) #f ns) (cadr args))))
|
||||
(else (error #f "symbol: wrong arity"))))
|
||||
|
||||
;; gensym: per-process counter.
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@
|
|||
(cond ((null? args) (make-arraylist '()))
|
||||
((number? (car args)) (make-arraylist '()))
|
||||
(else (make-arraylist (seq->list (jolt-seq (car args))))))))
|
||||
(register-host-methods! "arraylist"
|
||||
(define arraylist-methods
|
||||
(list
|
||||
(cons "add" (lambda (self . a)
|
||||
;; (.add x) -> append+true; (.add i x) -> insert at i, returns nil.
|
||||
|
|
@ -58,6 +58,14 @@
|
|||
(begin (al-push! self (car a)) #t)
|
||||
(begin (al-insert-at! self (jnum->exact (car a)) (cadr a)) jolt-nil))))
|
||||
(cons "add!" (lambda (self x) (al-push! self x) #t))
|
||||
(cons "addAll" (lambda (self . a)
|
||||
;; (.addAll coll) appends; (.addAll i coll) inserts at i.
|
||||
(let* ((at-i (= 2 (length a)))
|
||||
(i (if at-i (jnum->exact (car a)) (al-cnt self)))
|
||||
(coll (if at-i (cadr a) (car a))))
|
||||
(let loop ((xs (seq->list (jolt-seq coll))) (k i))
|
||||
(if (null? xs) (pair? (seq->list (jolt-seq coll)))
|
||||
(begin (al-insert-at! self k (car xs)) (loop (cdr xs) (fx+ k 1))))))))
|
||||
(cons "get" (lambda (self i) (vector-ref (al-vec self) (jnum->exact i))))
|
||||
(cons "set" (lambda (self i x)
|
||||
(let* ((idx (jnum->exact i)) (old (vector-ref (al-vec self) idx)))
|
||||
|
|
@ -72,6 +80,43 @@
|
|||
(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-host-methods! "arraylist" arraylist-methods)
|
||||
|
||||
;; java.util.LinkedList: the ArrayList backing plus the Deque surface
|
||||
;; (addFirst/addLast/removeFirst/removeLast/getFirst/getLast/peek/push/pop).
|
||||
;; tools.reader holds pending splice forms in one and (seq)s / .remove(0)s it.
|
||||
(define (al-first self) (vector-ref (al-vec self) 0))
|
||||
(define (al-last self) (vector-ref (al-vec self) (fx- (al-cnt self) 1)))
|
||||
(define linkedlist-methods
|
||||
(append arraylist-methods
|
||||
(list
|
||||
(cons "addFirst" (lambda (self x) (al-insert-at! self 0 x) jolt-nil))
|
||||
(cons "addLast" (lambda (self x) (al-push! self x) jolt-nil))
|
||||
(cons "offer" (lambda (self x) (al-push! self x) #t))
|
||||
(cons "removeFirst" (lambda (self) (let ((o (al-first self))) (al-remove-at! self 0) o)))
|
||||
(cons "removeLast" (lambda (self) (let ((o (al-last self))) (al-remove-at! self (fx- (al-cnt self) 1)) o)))
|
||||
(cons "getFirst" al-first) (cons "getLast" al-last)
|
||||
(cons "peek" (lambda (self) (if (fx=? 0 (al-cnt self)) jolt-nil (al-first self))))
|
||||
(cons "poll" (lambda (self) (if (fx=? 0 (al-cnt self)) jolt-nil (let ((o (al-first self))) (al-remove-at! self 0) o))))
|
||||
(cons "push" (lambda (self x) (al-insert-at! self 0 x) jolt-nil))
|
||||
(cons "pop" (lambda (self) (let ((o (al-first self))) (al-remove-at! self 0) o))))))
|
||||
(define (make-linkedlist xs)
|
||||
(let ((al (make-arraylist xs))) (make-jhost "linkedlist" (jhost-state al))))
|
||||
(register-host-methods! "linkedlist" linkedlist-methods)
|
||||
(let ((ctor (lambda args
|
||||
(cond ((null? args) (make-linkedlist '()))
|
||||
(else (make-linkedlist (seq->list (jolt-seq (car args)))))))))
|
||||
(register-class-ctor! "LinkedList" ctor)
|
||||
(register-class-ctor! "java.util.LinkedList" ctor))
|
||||
|
||||
;; ArrayList / LinkedList are Iterable: (seq al) walks the elements (nil if empty),
|
||||
;; so (seq pending-forms) and reduce/into over one work like the JVM.
|
||||
(define %al-seq jolt-seq)
|
||||
(set! jolt-seq
|
||||
(lambda (x)
|
||||
(if (and (jhost? x) (or (string=? (jhost-tag x) "arraylist") (string=? (jhost-tag x) "linkedlist")))
|
||||
(list->cseq (al->list x))
|
||||
(%al-seq x))))
|
||||
|
||||
;; Appendable.append text: append(x) renders x; append(csq,start,end) appends the
|
||||
;; subsequence csq[start,end) (data.json's writer appends string runs this way).
|
||||
|
|
@ -433,8 +478,12 @@
|
|||
(list->string (vector->list v)))))
|
||||
((string? x) x)
|
||||
(else (jolt-str-render-one x)))))
|
||||
;; (BigInteger. s) | (BigInteger. s radix) — parse a string in the given radix
|
||||
;; (default 10). tools.reader's integer parser builds (BigInteger. digits radix).
|
||||
(register-class-ctor! "BigInteger"
|
||||
(lambda (v) (parse-int-or-throw v 10 "BigInteger")))
|
||||
(lambda (v . r) (parse-int-or-throw v (if (null? r) 10 (jnum->exact (car r))) "BigInteger")))
|
||||
(register-class-ctor! "java.math.BigInteger"
|
||||
(lambda (v . r) (parse-int-or-throw v (if (null? r) 10 (jnum->exact (car r))) "BigInteger")))
|
||||
(register-class-ctor! "MapEntry" (lambda (k v) (make-map-entry k v)))
|
||||
;; JVM exception ctors -> a typed host throwable carrying the canonical :jolt/class
|
||||
;; (so class / instance? / getMessage / ex-message reflect the real type) and the
|
||||
|
|
@ -568,17 +617,29 @@
|
|||
(define %hs-rmd2 record-method-dispatch)
|
||||
(set! record-method-dispatch
|
||||
(lambda (obj method-name rest-args)
|
||||
(if (regex-t? obj)
|
||||
(let ((rest (if (jolt-nil? rest-args) '() (seq->list rest-args))))
|
||||
(cond ((string=? method-name "split")
|
||||
;; .split returns a String[] — a seq (prints
|
||||
;; (a b c), not a vector). re-split with no limit; drop trailing
|
||||
;; empties (JVM default).
|
||||
(let ((parts (re-split (regex-t-irx obj) (car rest) #f)))
|
||||
(list->cseq (str-split-drop-trailing parts))))
|
||||
((string=? method-name "pattern") (regex-t-source obj))
|
||||
(else (error #f (string-append "No method " method-name " on Pattern")))))
|
||||
(%hs-rmd2 obj method-name rest-args))))
|
||||
(let ((rest (if (jolt-nil? rest-args) '() (seq->list rest-args))))
|
||||
(cond
|
||||
((regex-t? obj)
|
||||
(cond ((string=? method-name "split")
|
||||
;; .split returns a String[] — a seq (prints
|
||||
;; (a b c), not a vector). re-split with no limit; drop trailing
|
||||
;; empties (JVM default).
|
||||
(let ((parts (re-split (regex-t-irx obj) (car rest) #f)))
|
||||
(list->cseq (str-split-drop-trailing parts))))
|
||||
((string=? method-name "pattern") (regex-t-source obj))
|
||||
((or (string=? method-name "toString")) (regex-t-source obj))
|
||||
;; (.matcher pattern s) -> a Matcher (matcher-t) for stepping matches.
|
||||
((string=? method-name "matcher") (jolt-re-matcher obj (car rest)))
|
||||
(else (error #f (string-append "No method " method-name " on Pattern")))))
|
||||
;; java.util.regex.Matcher: .matches (anchored whole-region), .find
|
||||
;; (next match), .group [n], .groupCount.
|
||||
((jolt-matcher? obj)
|
||||
(cond ((string=? method-name "matches") (jolt-matcher-matches obj))
|
||||
((string=? method-name "find") (not (jolt-nil? (jolt-re-find obj))))
|
||||
((string=? method-name "group") (apply jolt-matcher-group obj rest))
|
||||
((string=? method-name "groupCount") (jolt-matcher-group-count obj))
|
||||
(else (error #f (string-append "No method " method-name " on Matcher")))))
|
||||
(else (%hs-rmd2 obj method-name rest-args))))))
|
||||
|
||||
;; ---- def-var! the registry entry points so emit can also reach them ---------
|
||||
(def-var! "clojure.core" "host-static-ref" host-static-ref)
|
||||
|
|
|
|||
|
|
@ -96,6 +96,70 @@
|
|||
(register-class-statics! "PersistentArrayMap" (list (cons "createWithCheck" pam-create-with-check)))
|
||||
(register-class-statics! "clojure.lang.PersistentArrayMap" (list (cons "createWithCheck" pam-create-with-check)))
|
||||
|
||||
;; clojure.lang.RT/map: build a map from a [k v k v…] array/seq (RT.map). Small
|
||||
;; maps keep insertion order (PersistentArrayMap). tools.reader builds map and
|
||||
;; namespaced-map literals this way.
|
||||
(define (rt-map arr)
|
||||
(let loop ((xs (if (jolt-nil? arr) '() (seq->list (jolt-seq arr)))) (m (jolt-hash-map)))
|
||||
(cond ((null? xs) m)
|
||||
((null? (cdr xs)) (error #f "RT/map: odd key/value count"))
|
||||
(else (loop (cddr xs) (jolt-assoc m (car xs) (cadr xs)))))))
|
||||
(register-class-statics! "RT" (list (cons "map" rt-map)))
|
||||
(register-class-statics! "clojure.lang.RT" (list (cons "map" rt-map)))
|
||||
|
||||
;; clojure.lang.PersistentList/create: a list (in order) from a seq; empty -> ().
|
||||
(define (plist-create x)
|
||||
(let ((items (seq->list (jolt-seq x))))
|
||||
(if (null? items) jolt-empty-list (list->cseq items))))
|
||||
(register-class-statics! "PersistentList" (list (cons "create" plist-create)))
|
||||
(register-class-statics! "clojure.lang.PersistentList" (list (cons "create" plist-create)))
|
||||
|
||||
;; clojure.lang.PersistentHashSet/createWithCheck: a set from a seq, throwing on a
|
||||
;; duplicate element (tools.reader's #{…} reader reports the dup).
|
||||
(define (phs-create-with-check x)
|
||||
(let loop ((xs (seq->list (jolt-seq x))) (s (jolt-hash-set)))
|
||||
(if (null? xs) s
|
||||
(let ((e (car xs)))
|
||||
(if (jolt-truthy? (jolt-contains? s e))
|
||||
(jolt-throw (jolt-ex-info (string-append "Duplicate key: " (jolt-str-render-one e)) (jolt-hash-map)))
|
||||
(loop (cdr xs) (jolt-conj1 s e)))))))
|
||||
(register-class-statics! "PersistentHashSet" (list (cons "createWithCheck" phs-create-with-check)))
|
||||
(register-class-statics! "clojure.lang.PersistentHashSet" (list (cons "createWithCheck" phs-create-with-check)))
|
||||
|
||||
;; java.lang.Character statics. digit(ch, radix) -> the digit value or -1; ch may
|
||||
;; be a char or an int codepoint (tools.reader passes (int c)). isDigit/
|
||||
;; isWhitespace take a char; valueOf boxes a char (identity on jolt).
|
||||
(define (char->cp x) (if (char? x) (char->integer x) (jnum->exact x)))
|
||||
(define (char-digit-value cp radix)
|
||||
(let ((d (cond ((and (fx>=? cp 48) (fx<=? cp 57)) (fx- cp 48)) ; 0-9
|
||||
((and (fx>=? cp 97) (fx<=? cp 122)) (fx+ 10 (fx- cp 97))) ; a-z
|
||||
((and (fx>=? cp 65) (fx<=? cp 90)) (fx+ 10 (fx- cp 65))) ; A-Z
|
||||
(else 99))))
|
||||
(if (fx<? d radix) d -1)))
|
||||
(define character-statics
|
||||
(list (cons "digit" (lambda (ch radix) (->num (char-digit-value (char->cp ch) (jnum->exact radix)))))
|
||||
(cons "isDigit" (lambda (ch) (let ((cp (char->cp ch))) (and (fx>=? cp 48) (fx<=? cp 57)))))
|
||||
(cons "isWhitespace" (lambda (ch) (char-whitespace? (integer->char (char->cp ch)))))
|
||||
(cons "valueOf" (lambda (ch) (if (char? ch) ch (integer->char (char->cp ch)))))))
|
||||
(register-class-statics! "Character" character-statics)
|
||||
(register-class-statics! "java.lang.Character" character-statics)
|
||||
|
||||
;; java.util.regex.Pattern/compile: a regex value from a string pattern.
|
||||
(define pattern-statics (list (cons "compile" (lambda (s) (jolt-regex (jolt-str-render-one s))))))
|
||||
(register-class-statics! "Pattern" pattern-statics)
|
||||
(register-class-statics! "java.util.regex.Pattern" pattern-statics)
|
||||
|
||||
;; clojure.lang.BigInt / clojure.lang.Numbers: jolt has one exact-integer type
|
||||
;; (Chez bignums auto-reduce), so BigInt.fromBigInteger and Numbers.reduceBigInt
|
||||
;; are identity. tools.reader's number parser threads integers through these.
|
||||
(define identity-num-statics (list (cons "fromBigInteger" (lambda (x) x))))
|
||||
(register-class-statics! "BigInt" identity-num-statics)
|
||||
(register-class-statics! "clojure.lang.BigInt" identity-num-statics)
|
||||
(register-class-statics! "Numbers"
|
||||
(list (cons "reduceBigInt" (lambda (x) x)) (cons "toRatio" (lambda (x) x))))
|
||||
(register-class-statics! "clojure.lang.Numbers"
|
||||
(list (cons "reduceBigInt" (lambda (x) x)) (cons "toRatio" (lambda (x) x))))
|
||||
|
||||
(define (now-millis)
|
||||
(let ((t (current-time 'time-utc)))
|
||||
(+ (* 1000 (time-second t)) (quotient (time-nanosecond t) 1000000))))
|
||||
|
|
|
|||
|
|
@ -88,6 +88,13 @@
|
|||
;; Double/Float .isNaN / .isInfinite (a non-flonum is neither).
|
||||
((string=? method "isNaN") (and (flonum? n) (not (= n n))))
|
||||
((string=? method "isInfinite") (and (flonum? n) (infinite? n)))
|
||||
;; BigInteger interop: .negate / .bitLength / .signum / .abs. A jolt integer is
|
||||
;; a Chez exact integer, so these are native (integer-length = JVM bitLength,
|
||||
;; matching for negative values too). tools.reader's number parser uses them.
|
||||
((string=? method "negate") (->num (- (jnum->exact n))))
|
||||
((string=? method "abs") (->num (abs (jnum->exact n))))
|
||||
((string=? method "bitLength") (->num (integer-length (jnum->exact n))))
|
||||
((string=? method "signum") (->num (let ((e (jnum->exact n))) (cond ((> e 0) 1) ((< e 0) -1) (else 0)))))
|
||||
(else (error #f (string-append "No method " method " for number")))))
|
||||
|
||||
;; Mutable static fields: "Class" -> (member -> 1-vector cell). A library that
|
||||
|
|
|
|||
|
|
@ -264,13 +264,28 @@
|
|||
(def-var! "clojure.core" "use" loader-use)
|
||||
|
||||
(def-var! "clojure.core" "load-file" jolt-load-file)
|
||||
;; load: each arg is a "/"-rooted resource path like "/app/extra"; load the file
|
||||
;; for it relative to the search roots (strip the leading slash, try .clj/.cljc).
|
||||
|
||||
;; The directory of a namespace's resource path: "clojure.tools.reader-test" ->
|
||||
;; "clojure/tools" (drop the last segment of ns-name->rel). "" for a top-level ns.
|
||||
(define (ns-rel-dir name)
|
||||
(let* ((r (ns-name->rel name)))
|
||||
(let loop ((k (fx- (string-length r) 1)))
|
||||
(cond ((fx<? k 0) "")
|
||||
((char=? (string-ref r k) #\/) (substring r 0 k))
|
||||
(else (loop (fx- k 1)))))))
|
||||
|
||||
;; load: an arg starting with "/" is a root-relative resource path ("/app/extra");
|
||||
;; otherwise it is resolved against the CURRENT namespace's directory, matching
|
||||
;; Clojure — (load "common_tests") from clojure.tools.reader-test loads
|
||||
;; clojure/tools/common_tests.clj. Strip the leading slash / try .clj/.cljc.
|
||||
(define (jolt-load . paths)
|
||||
(for-each
|
||||
(lambda (p)
|
||||
(let* ((rel (if (and (> (string-length p) 0) (char=? (string-ref p 0) #\/))
|
||||
(substring p 1 (string-length p)) p))
|
||||
(let* ((rel (cond
|
||||
((and (> (string-length p) 0) (char=? (string-ref p 0) #\/))
|
||||
(substring p 1 (string-length p)))
|
||||
(else (let ((dir (ns-rel-dir (chez-current-ns))))
|
||||
(if (string=? dir "") p (string-append dir "/" p))))))
|
||||
(f (resolve-on-roots rel)))
|
||||
(if f (load-jolt-file f)
|
||||
(error #f "Could not locate resource on source roots" p))))
|
||||
|
|
|
|||
|
|
@ -47,6 +47,11 @@
|
|||
(memv c '(#\( #\) #\[ #\] #\{ #\} #\" #\; #\@ #\^ #\` #\~ #\\))))
|
||||
|
||||
(define (rdr-digit? c) (and (char>=? c #\0) (char<=? c #\9)))
|
||||
(define (rdr-octal? c) (and (char>=? c #\0) (char<=? c #\7)))
|
||||
;; every char of s in [from,to) is an octal digit (and the span is non-empty).
|
||||
(define (rdr-all-octal? s from to)
|
||||
(and (fx<? from to)
|
||||
(let loop ((i from)) (cond ((fx=? i to) #t) ((rdr-octal? (string-ref s i)) (loop (fx+ i 1))) (else #f)))))
|
||||
|
||||
;; Advance past whitespace, commas, and ;-to-end-of-line comments.
|
||||
(define (rdr-skip-ws s i end)
|
||||
|
|
@ -129,6 +134,11 @@
|
|||
(and radix (integer? radix) (>= radix 2) (<= radix 36)
|
||||
(let ((v (rdr-parse-radix (substring body (+ ri 1) blen) radix)))
|
||||
(and v (* sign v)))))))
|
||||
;; octal 0NNN: a leading 0 followed by octal digits (Clojure reads 042 as 34,
|
||||
;; not decimal 42). "0" alone, 0x.., 0r.. and a float "0.5" are handled
|
||||
;; elsewhere or fall through (a non-octal digit fails rdr-all-octal?).
|
||||
((and (>= blen 2) (char=? (string-ref body 0) #\0) (rdr-all-octal? body 1 blen))
|
||||
(let ((o (rdr-parse-radix (substring body 1 blen) 8))) (and o (* sign o))))
|
||||
;; bigint suffix N
|
||||
((and (> blen 1) (char=? (string-ref body (- blen 1)) #\N))
|
||||
(let ((n (string->number (substring body 0 (- blen 1)))))
|
||||
|
|
@ -174,7 +184,13 @@
|
|||
((#\") (loop (+ i 2) (cons #\" acc)))
|
||||
((#\b) (loop (+ i 2) (cons #\backspace acc)))
|
||||
((#\f) (loop (+ i 2) (cons #\page acc)))
|
||||
((#\0) (loop (+ i 2) (cons #\nul acc)))
|
||||
;; octal escape \ooo: 1-3 octal digits (Clojure's \0..\377), so \000
|
||||
;; is one null char, not \0 + literal "00".
|
||||
((#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7)
|
||||
(let oct ((j (+ i 1)) (val 0) (cnt 0))
|
||||
(if (and (fx<? cnt 3) (fx<? j end) (rdr-octal? (string-ref s j)))
|
||||
(oct (fx+ j 1) (fx+ (fx* val 8) (fx- (char->integer (string-ref s j)) 48)) (fx+ cnt 1))
|
||||
(loop j (cons (integer->char val) acc)))))
|
||||
((#\u)
|
||||
(let-values (((cp j) (rdr-hex->int s (+ i 2) 4)))
|
||||
;; A \u escape is a UTF-16 code unit. jolt chars are Unicode scalars,
|
||||
|
|
|
|||
|
|
@ -221,6 +221,21 @@
|
|||
(if last (irx-result last)
|
||||
(jolt-throw (jolt-ex-info "No match found" (jolt-hash-map))))))
|
||||
|
||||
;; java.util.regex.Matcher methods over a matcher-t. .matches anchors a full-region
|
||||
;; match and remembers it for .group; .group n returns submatch n (0 = whole) or
|
||||
;; nil; .groupCount is the pattern's capturing-group count.
|
||||
(define (jolt-matcher-matches m)
|
||||
(let ((mm (irregex-match (matcher-t-irx m) (matcher-t-str m))))
|
||||
(matcher-t-last-set! m mm)
|
||||
(if mm #t #f)))
|
||||
(define (jolt-matcher-group m . n)
|
||||
(let ((last (matcher-t-last m)))
|
||||
(if last
|
||||
(let ((s (irregex-match-substring last (if (pair? n) (->idx (car n)) 0))))
|
||||
(if s s jolt-nil))
|
||||
(jolt-throw (jolt-ex-info "No match available" (jolt-hash-map))))))
|
||||
(define (jolt-matcher-group-count m) (irregex-num-submatches (matcher-t-irx m)))
|
||||
|
||||
;; All non-overlapping matches, left to right. Advance past each match end (or by
|
||||
;; one on a zero-width match). nil when there are no matches (Clojure: seq-able as
|
||||
;; nil, so (if-let [m (re-seq ...)] ...) works).
|
||||
|
|
|
|||
|
|
@ -107,7 +107,8 @@
|
|||
|
||||
;; (is (thrown-with-msg? Class re body...))
|
||||
(thrown-form? form "thrown-with-msg?")
|
||||
(let [klass (name (second form))
|
||||
(let [klass-sym (second form)
|
||||
klass (name klass-sym)
|
||||
re (nth form 2)
|
||||
body (nthrest form 3)]
|
||||
`(try
|
||||
|
|
@ -115,7 +116,11 @@
|
|||
(clojure.test/fail! (str "expected " '~form " to throw"))
|
||||
(catch Throwable e#
|
||||
(let [m# (or (clojure.core/ex-message e#) (str e#))]
|
||||
(if (and (clojure.test/class-match? e# ~klass) (re-find ~re m#))
|
||||
;; honor the class hierarchy (ExceptionInfo IS a RuntimeException),
|
||||
;; then fall back to a simple-name match like thrown? does.
|
||||
(if (and (or (clojure.core/instance? ~klass-sym e#)
|
||||
(clojure.test/class-match? e# ~klass))
|
||||
(re-find ~re m#))
|
||||
(clojure.test/inc-pass!)
|
||||
(clojure.test/fail! (str "expected throw of " ~klass " matching " ~re " but got " (clojure.core/class e#) ": " m#)))))))
|
||||
|
||||
|
|
|
|||
|
|
@ -3357,4 +3357,12 @@
|
|||
{:suite "host-interop / instance? Object" :label "nil is not an instance of Object" :expected "false" :actual "(instance? Object nil)"}
|
||||
{:suite "host-interop / instance? Object" :label "java.lang.Object too" :expected "true" :actual "(instance? java.lang.Object :k)"}
|
||||
{:suite "host-interop / Compiler" :label "@Compiler/LINE is a number" :expected "true" :actual "(number? @clojure.lang.Compiler/LINE)"}
|
||||
{:suite "reader / octal literals" :label "0NNN is octal" :expected "34" :actual "042"}
|
||||
{:suite "reader / octal literals" :label "negative octal" :expected "-34" :actual "-042"}
|
||||
{:suite "reader / octal literals" :label "octal char escape \\oNNN" :expected "true" :actual "(= (char 0377) \\o377)"}
|
||||
{:suite "reader / octal literals" :label "octal string escape is one char" :expected "true" :actual "(= \"a\\000b\" (str \"a\" (char 0) \"b\"))"}
|
||||
{:suite "reader / octal literals" :label "hex still decimal-distinct" :expected "[1070 255 0]" :actual "[0x42e 0xff 0]"}
|
||||
{:suite "symbols / construction" :label "(symbol nil name) equals (symbol name)" :expected "true" :actual "(= (symbol nil \"foo\") (quote foo))"}
|
||||
{:suite "symbols / construction" :label "(symbol nil name) has no namespace" :expected "true" :actual "(nil? (namespace (symbol nil \"x\")))"}
|
||||
{:suite "host-interop / instance? hierarchy" :label "ExceptionInfo is a RuntimeException" :expected "[true true true]" :actual "[(instance? RuntimeException (ex-info \"x\" {})) (instance? Exception (ex-info \"x\" {})) (instance? Throwable (ex-info \"x\" {}))]"}
|
||||
]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue