clojure.string toString coercion; some-fn/ifn? reference semantics; misc host gaps
The clojure.string case fns and searches now take any Object s through its toString like the reference's ^CharSequence signatures ((upper-case :kw) is ":KW", (capitalize 1) is "1"); nil throws, and a nil substr in starts-with?/ends-with? throws. some-fn re-ported with the reference arities: (some-fn) is an arity error and a no-match result is the last predicate's own falsy value (false, not nil). ifn? covers multimethods, promises (which are now invocable — calling one delivers, via a cold-path invoke-arm registry that costs the hot dispatch nothing), and deftypes implementing IFn's invoke. One structural find on the way: defmulti/defmethod deferred inside a fn body (the deftest pattern) interned/resolved in whatever namespace was current when they RAN, not the one they were written in — the macros now bake their expansion ns and the setups honor it. Also: Boolean/Integer/Double wrapper ctors, primitive TYPE statics (Integer/TYPE etc.), .reduce on collections (IReduce), and Long/TYPE. cts baseline 5857 -> 5904 pass, 58 -> 28 errors, 57 baselined namespaces — the string cluster, some-fn, ifn-qmark, boolean-qmark, and reduce namespaces are all fully clean. 7 JVM-certified corpus rows; spec entry.
This commit is contained in:
parent
a9542077fc
commit
e17bcfd0af
15 changed files with 1100 additions and 941 deletions
|
|
@ -321,6 +321,39 @@ in the watch namespaces is their STM `ref` section — refs are out of scope,
|
|||
|
||||
---
|
||||
|
||||
### clojure.string coercion, some-fn, ifn? — since 1.2/1.3
|
||||
|
||||
```
|
||||
(clojure.string/upper-case s) … (some-fn p & ps) (ifn? x)
|
||||
```
|
||||
|
||||
**Semantics**
|
||||
|
||||
- S1. The clojure.string case fns and searches (`upper-case`, `lower-case`,
|
||||
`capitalize`, `starts-with?`, `ends-with?`, `includes?`, `index-of`,
|
||||
`replace`) take any Object `s` through its `toString`, like the reference's
|
||||
`^CharSequence`+`.toString` signatures: `(upper-case :kw)` is `":KW"`,
|
||||
`(capitalize 1)` is `"1"`. nil throws (method call on null); a nil `substr`
|
||||
throws.
|
||||
- S2. `some-fn` follows the reference arities: at least one predicate
|
||||
(`(some-fn)` is an arity error) and the returned fn chains with `or`, so a
|
||||
no-match result is the last predicate's own falsy value (`false` stays
|
||||
`false`).
|
||||
- S3. `ifn?` covers fns, keywords, symbols, maps, sets, vectors, vars,
|
||||
multimethods, promises (invoking a promise delivers it), and a
|
||||
deftype/defrecord implementing `clojure.lang.IFn`'s `invoke`.
|
||||
- S4. A `defmulti`/`defmethod` deferred inside a fn body interns/resolves in
|
||||
the namespace it was WRITTEN in (the macros bake their expansion ns), not
|
||||
whatever namespace is current when it runs.
|
||||
|
||||
**Conformance**
|
||||
|
||||
S1–S4 → corpus `string / toString coercion`, `core / some-fn`, `core / ifn?`,
|
||||
`multimethods / deferred definition`; clojure-test-suite string/some-fn/
|
||||
ifn-qmark/boolean-qmark/reduce namespaces (all fully passing).
|
||||
|
||||
---
|
||||
|
||||
## Authoring notes
|
||||
|
||||
- Source examples from the ClojureDocs export (`clojuredocs-export.edn`,
|
||||
|
|
|
|||
|
|
@ -301,6 +301,16 @@
|
|||
(def-var! "clojure.core" "future-cancelled?" jolt-native-future-cancelled?)
|
||||
(def-var! "clojure.core" "promise" jolt-promise-new)
|
||||
(def-var! "clojure.core" "deliver" jolt-deliver)
|
||||
;; a promise is an IFn on the JVM: (p val) delivers. Registered as a cold
|
||||
;; invoke arm; callable-host? feeds the ifn? overlay (multimethods included).
|
||||
(register-invoke-arm! jolt-promise?
|
||||
(lambda (p args)
|
||||
(if (and (pair? args) (null? (cdr args)))
|
||||
(jolt-deliver p (car args))
|
||||
(jolt-throw (jolt-host-throwable "clojure.lang.ArityException"
|
||||
"Wrong number of args passed to a promise")))))
|
||||
(def-var! "jolt.host" "callable-host?"
|
||||
(lambda (x) (if (or (jolt-multifn? x) (jolt-promise? x)) #t jolt-nil)))
|
||||
(def-var! "clojure.core" "agent" jolt-agent-new)
|
||||
(def-var! "clojure.core" "agent?" jolt-agent?)
|
||||
(def-var! "clojure.core" "send" jolt-agent-send)
|
||||
|
|
|
|||
|
|
@ -60,6 +60,12 @@
|
|||
;; branch and is mis-read as a missing :iterator key (nil). Some libraries
|
||||
;; (e.g. malli's -vmap) iterate a map this way.
|
||||
((string=? name "iterator") (list (make-jiterator (jolt-seq obj))))
|
||||
;; (.reduce coll f) / (.reduce coll f init): clojure.lang.IReduce — every
|
||||
;; persistent collection reduces itself on the JVM.
|
||||
((string=? name "reduce")
|
||||
(list (if (pair? (cdr args))
|
||||
(jolt-reduce (car args) (cadr args) obj)
|
||||
(jolt-reduce (car args) obj))))
|
||||
(else #f)))
|
||||
|
||||
;; Universal object-methods: on a
|
||||
|
|
|
|||
|
|
@ -202,7 +202,8 @@
|
|||
(bitwise-ior (bitwise-arithmetic-shift-left r 1)
|
||||
(bitwise-and (bitwise-arithmetic-shift-right v i) 1)))))))
|
||||
(register-class-statics! "Long"
|
||||
(list (cons "MAX_VALUE" (->num 9223372036854775807))
|
||||
(list (cons "TYPE" "long")
|
||||
(cons "MAX_VALUE" (->num 9223372036854775807))
|
||||
(cons "MIN_VALUE" (->num -9223372036854775808))
|
||||
(cons "bitCount" (lambda (n) (->num (bitwise-bit-count (bitwise-and (jnum->exact n) long-mask64)))))
|
||||
(cons "numberOfLeadingZeros" (lambda (n) (->num (long-nlz n))))
|
||||
|
|
@ -214,6 +215,8 @@
|
|||
(define (int->u32 n) (if (< n 0) (+ n 4294967296) n))
|
||||
(register-class-statics! "Integer"
|
||||
(list (cons "MAX_VALUE" (->num 2147483647)) (cons "MIN_VALUE" (->num -2147483648))
|
||||
;; the primitive class token (int.class); jolt models a class as its name
|
||||
(cons "TYPE" "int")
|
||||
(cons "valueOf" (lambda (x . r)
|
||||
(if (number? x) (->num x)
|
||||
(parse-int-or-throw x (if (null? r) 10 (jnum->exact (car r))) "valueOf"))))
|
||||
|
|
@ -227,12 +230,14 @@
|
|||
;; Byte / Short bounds (their values are plain integers on jolt; the statics let
|
||||
;; libraries reference the JVM ranges — clojure.test.check generates over them).
|
||||
(register-class-statics! "Byte"
|
||||
(list (cons "MAX_VALUE" (->num 127)) (cons "MIN_VALUE" (->num -128))
|
||||
(list (cons "TYPE" "byte")
|
||||
(cons "MAX_VALUE" (->num 127)) (cons "MIN_VALUE" (->num -128))
|
||||
(cons "valueOf" (lambda (x . r) (->num (if (number? x) x (parse-int-or-throw x 10 "valueOf")))))
|
||||
(cons "parseByte" (lambda (x . r) (parse-int-or-throw x (if (null? r) 10 (jnum->exact (car r))) "parseByte")))
|
||||
(cons "toString" (lambda (x . r) (number->string (jnum->exact x))))))
|
||||
(register-class-statics! "Short"
|
||||
(list (cons "MAX_VALUE" (->num 32767)) (cons "MIN_VALUE" (->num -32768))
|
||||
(list (cons "TYPE" "short")
|
||||
(cons "MAX_VALUE" (->num 32767)) (cons "MIN_VALUE" (->num -32768))
|
||||
(cons "valueOf" (lambda (x . r) (->num (if (number? x) x (parse-int-or-throw x 10 "valueOf")))))
|
||||
(cons "parseShort" (lambda (x . r) (parse-int-or-throw x (if (null? r) 10 (jnum->exact (car r))) "parseShort")))
|
||||
(cons "toString" (lambda (x . r) (number->string (jnum->exact x))))))
|
||||
|
|
@ -247,13 +252,15 @@
|
|||
(cons "ROOT" "und") (cons "US" "en-US") (cons "ENGLISH" "en")))
|
||||
|
||||
(register-class-statics! "Boolean"
|
||||
(list (cons "parseBoolean" (lambda (s) (string=? "true" (ascii-string-down (if (string? s) s (jolt-str-render-one s))))))
|
||||
(list (cons "TYPE" "boolean")
|
||||
(cons "parseBoolean" (lambda (s) (string=? "true" (ascii-string-down (if (string? s) s (jolt-str-render-one s))))))
|
||||
(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)
|
||||
(list (cons "TYPE" "double")
|
||||
(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))))
|
||||
|
|
@ -261,11 +268,13 @@
|
|||
(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)))
|
||||
(list (cons "TYPE" "float")
|
||||
(cons "parseFloat" parse-double-or-throw) (cons "valueOf" ->double)))
|
||||
|
||||
;; Character: ASCII predicates (the engine is byte/ASCII oriented).
|
||||
(register-class-statics! "Character"
|
||||
(list (cons "isUpperCase" (lambda (c) (let ((n (char-code c))) (and (>= n 65) (<= n 90)))))
|
||||
(list (cons "TYPE" "char")
|
||||
(cons "isUpperCase" (lambda (c) (let ((n (char-code c))) (and (>= n 65) (<= n 90)))))
|
||||
(cons "isLowerCase" (lambda (c) (let ((n (char-code c))) (and (>= n 97) (<= n 122)))))
|
||||
(cons "isDigit" (lambda (c) (let ((n (char-code c))) (and (>= n 48) (<= n 57)))))
|
||||
;; JVM Character.isWhitespace: Unicode whitespace (so U+2028 line separator
|
||||
|
|
|
|||
|
|
@ -624,6 +624,31 @@
|
|||
;; (Long. n) / (Long. "n"): a Long is just jolt's integer; return it (parse a string).
|
||||
(register-class-ctor! "Long" (lambda (x) (if (string? x) (parse-int-or-throw x 10 "Long") (->num (jnum->exact x)))))
|
||||
(register-class-ctor! "java.lang.Long" (lambda (x) (if (string? x) (parse-int-or-throw x 10 "Long") (->num (jnum->exact x)))))
|
||||
;; (Integer. n) / (Integer. "n"): jolt's integer, range-checked like intCast.
|
||||
(define (integer-ctor x)
|
||||
(jolt-int-cast (if (string? x) (parse-int-or-throw x 10 "Integer") x)))
|
||||
(register-class-ctor! "Integer" integer-ctor)
|
||||
(register-class-ctor! "java.lang.Integer" integer-ctor)
|
||||
;; (Double. x) / (Double. "x"): jolt's double.
|
||||
(define (double-ctor x)
|
||||
(if (string? x)
|
||||
(let ((n (string->number x)))
|
||||
(if n (exact->inexact n)
|
||||
(jolt-throw (jolt-host-throwable "java.lang.NumberFormatException"
|
||||
(string-append "For input string: \"" x "\"")))))
|
||||
(jolt-double x)))
|
||||
(register-class-ctor! "Double" double-ctor)
|
||||
(register-class-ctor! "java.lang.Double" double-ctor)
|
||||
|
||||
;; (Boolean. "true") / (Boolean. b): true for the string "true" (case-insensitive,
|
||||
;; anything else false) or the boolean itself — Boolean.valueOf semantics; the
|
||||
;; box is jolt's plain boolean.
|
||||
(define (boolean-ctor x)
|
||||
(cond ((string? x) (string-ci=? x "true"))
|
||||
((boolean? x) x)
|
||||
(else #f)))
|
||||
(register-class-ctor! "Boolean" boolean-ctor)
|
||||
(register-class-ctor! "java.lang.Boolean" boolean-ctor)
|
||||
|
||||
;; --- java.net.URI -----------------------------------------------------------
|
||||
;; A minimal RFC-3986 split into scheme/authority/host/port/path/query/fragment,
|
||||
|
|
|
|||
|
|
@ -61,26 +61,36 @@
|
|||
|
||||
(define (jolt-defmulti-setup name-sym dispatch . opts)
|
||||
(let-values (((dk h) (parse-mm-opts opts)))
|
||||
(let ((mf (make-jolt-multifn (symbol-t-name name-sym) dispatch
|
||||
(let* ((sns (symbol-t-ns name-sym))
|
||||
;; the macro qualifies the name with its EXPANSION ns, so a defmulti
|
||||
;; deferred inside a fn (a deftest body) still defines in the ns it
|
||||
;; was written in, not whatever ns is current when it finally runs.
|
||||
(ns (if (string? sns) sns (chez-current-ns)))
|
||||
(mf (make-jolt-multifn (symbol-t-name name-sym) dispatch
|
||||
(new-mm-table) dk h (new-mm-table))))
|
||||
(def-var! (chez-current-ns) (symbol-t-name name-sym) mf)
|
||||
(def-var! ns (symbol-t-name name-sym) mf)
|
||||
mf)))
|
||||
|
||||
;; (defmethod-setup 'mm dispatch-val impl) — add a method. Auto-creates the multifn
|
||||
;; if absent (defmethod before defmulti — rare; identity dispatch as a fallback).
|
||||
(define (jolt-defmethod-setup mm-sym dval impl)
|
||||
(define (jolt-defmethod-setup mm-sym dval impl . rest)
|
||||
(let* ((nm (symbol-t-name mm-sym))
|
||||
(sns (symbol-t-ns mm-sym))
|
||||
(qns (and sns (not (jolt-nil? sns)) (not (null? sns)) sns))
|
||||
;; the macro passes its EXPANSION ns so a defmethod deferred inside a
|
||||
;; fn resolves like the JVM (against the ns it was written in, not the
|
||||
;; ns current when it runs); absent (old emitted code) fall back to the
|
||||
;; runtime ns.
|
||||
(here (if (and (pair? rest) (string? (car rest))) (car rest) (chez-current-ns)))
|
||||
;; qualified (cf.mm/ext) resolves in its own ns (cross-ns defmethod);
|
||||
;; unqualified resolves in the current ns, else a :refer's home ns (so a
|
||||
;; unqualified resolves in the writing ns, else a :refer's home ns (so a
|
||||
;; defmethod on a referred multifn lands on the real one), else stays in
|
||||
;; the current ns (a shadow, as before).
|
||||
;; the writing ns (a shadow, as before).
|
||||
(mns (cond
|
||||
(qns (or (chez-resolve-alias (chez-current-ns) qns) qns))
|
||||
((var-cell-lookup (chez-current-ns) nm) (chez-current-ns))
|
||||
((chez-resolve-refer (chez-current-ns) nm) => values)
|
||||
(else (chez-current-ns))))
|
||||
(qns (or (chez-resolve-alias here qns) qns))
|
||||
((var-cell-lookup here nm) here)
|
||||
((chez-resolve-refer here nm) => values)
|
||||
(else here)))
|
||||
(cur (var-deref mns nm))
|
||||
(mf (if (jolt-multifn? cur) cur
|
||||
;; auto-create: copy the dispatch fn + default from a same-named
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -436,6 +436,16 @@
|
|||
;; can't statically resolve to a procedure (a keyword/coll/proc held in a local)
|
||||
;; routes here. Off the arithmetic/self-recursion hot path by construction.
|
||||
;; ============================================================================
|
||||
;; (pred . handler) arms making a host type invocable; handler gets (f args).
|
||||
(define jolt-invoke-arms '())
|
||||
(define (register-invoke-arm! pred handler)
|
||||
(set! jolt-invoke-arms (cons (cons pred handler) jolt-invoke-arms)))
|
||||
(define (jolt-invoke-arm-for f)
|
||||
(let loop ((as jolt-invoke-arms))
|
||||
(cond ((null? as) #f)
|
||||
(((caar as) f) (cdar as))
|
||||
(else (loop (cdr as))))))
|
||||
|
||||
(define (jolt-invoke f . args)
|
||||
(cond
|
||||
((procedure? f) (apply f args))
|
||||
|
|
@ -449,6 +459,9 @@
|
|||
=> (lambda (m) (apply jolt-invoke m f args)))
|
||||
((and (reified-methods f) (hashtable-ref (reified-methods f) "invoke" #f))
|
||||
=> (lambda (m) (apply jolt-invoke m f args)))
|
||||
;; host types registered as callable (promise delivers, …): consulted only
|
||||
;; after every built-in case missed, so the hot dispatch pays nothing.
|
||||
((jolt-invoke-arm-for f) => (lambda (h) (h f args)))
|
||||
;; calling a non-fn: a ClassCastException naming the operator's CLASS (like
|
||||
;; the JVM's "class clojure.lang.LazySeq cannot be cast to ... IFn" — never
|
||||
;; the value, whose printed form may be unbounded: ((range)) must throw, not
|
||||
|
|
|
|||
|
|
@ -155,8 +155,43 @@
|
|||
(when-let [s (seq coll)]
|
||||
(or (pred (first s)) (recur pred (next s)))))
|
||||
|
||||
(defn some-fn [& preds]
|
||||
(fn [& xs] (some (fn [p] (some p xs)) preds)))
|
||||
;; Reference arities: at least one predicate ((some-fn) is an arity error), and
|
||||
;; the returned fn chains with or — a no-match result is the last predicate's
|
||||
;; own falsy value (false stays false, not nil).
|
||||
(defn some-fn
|
||||
([p]
|
||||
(fn sp1
|
||||
([] nil)
|
||||
([x] (p x))
|
||||
([x y] (or (p x) (p y)))
|
||||
([x y z] (or (p x) (p y) (p z)))
|
||||
([x y z & args] (or (sp1 x y z)
|
||||
(some p args)))))
|
||||
([p1 p2]
|
||||
(fn sp2
|
||||
([] nil)
|
||||
([x] (or (p1 x) (p2 x)))
|
||||
([x y] (or (p1 x) (p1 y) (p2 x) (p2 y)))
|
||||
([x y z] (or (p1 x) (p1 y) (p1 z) (p2 x) (p2 y) (p2 z)))
|
||||
([x y z & args] (or (sp2 x y z)
|
||||
(some (fn [q] (or (p1 q) (p2 q))) args)))))
|
||||
([p1 p2 p3]
|
||||
(fn sp3
|
||||
([] nil)
|
||||
([x] (or (p1 x) (p2 x) (p3 x)))
|
||||
([x y] (or (p1 x) (p2 x) (p3 x) (p1 y) (p2 y) (p3 y)))
|
||||
([x y z] (or (p1 x) (p2 x) (p3 x) (p1 y) (p2 y) (p3 y) (p1 z) (p2 z) (p3 z)))
|
||||
([x y z & args] (or (sp3 x y z)
|
||||
(some (fn [q] (or (p1 q) (p2 q) (p3 q))) args)))))
|
||||
([p1 p2 p3 & ps]
|
||||
(let [ps (cons p1 (cons p2 (cons p3 ps)))]
|
||||
(fn spn
|
||||
([] nil)
|
||||
([x] (some (fn [p] (p x)) ps))
|
||||
([x y] (or (spn x) (spn y)))
|
||||
([x y z] (or (spn x) (spn y) (spn z)))
|
||||
([x y z & args] (or (spn x y z)
|
||||
(some (fn [p] (some p args)) ps)))))))
|
||||
|
||||
(defn not-any? [pred coll] (not (some pred coll)))
|
||||
|
||||
|
|
|
|||
|
|
@ -183,10 +183,16 @@
|
|||
([x y z & args] (f (apply g x y z args)))))
|
||||
([f g & fs] (reduce comp (comp f g) fs)))
|
||||
|
||||
;; Canonical IFn set: fns, keywords, symbols, maps (sorted incl.),
|
||||
;; sets, vectors, and vars — NOT lists ((ifn? '(1 2)) is false in Clojure).
|
||||
;; Canonical IFn set: fns, keywords, symbols, maps (sorted incl.), sets,
|
||||
;; vectors, vars — NOT lists ((ifn? '(1 2)) is false in Clojure) — plus the
|
||||
;; host callables (multimethods, promises) and a deftype/record implementing
|
||||
;; clojure.lang.IFn's invoke.
|
||||
(defn ifn? [x]
|
||||
(or (fn? x) (keyword? x) (symbol? x) (map? x) (set? x) (vector? x) (var? x)))
|
||||
(if (or (fn? x) (keyword? x) (symbol? x) (map? x) (set? x) (vector? x) (var? x)
|
||||
(jolt.host/callable-host? x)
|
||||
(jolt.host/jrec-method? x "invoke"))
|
||||
true
|
||||
false))
|
||||
|
||||
;; Auto-promoting (') and unchecked arithmetic. Jolt numbers don't overflow,
|
||||
;; so all of these are the checked ops; fixed arities mirror Clojure's
|
||||
|
|
|
|||
|
|
@ -28,11 +28,18 @@
|
|||
(let [args (if (string? (first args)) (rest args) args)
|
||||
args (if (and (map? (first args)) (not (symbol? (first args)))) (rest args) args)
|
||||
dispatch (first args)
|
||||
opts (rest args)]
|
||||
`(defmulti-setup (quote ~name) ~dispatch ~@opts)))
|
||||
opts (rest args)
|
||||
;; qualify with the EXPANSION ns: a defmulti deferred inside a fn (a
|
||||
;; deftest body) must still define in the ns it was written in.
|
||||
qname (symbol (str (clojure.core/ns-name clojure.core/*ns*))
|
||||
(clojure.core/name name))]
|
||||
`(defmulti-setup (quote ~qname) ~dispatch ~@opts)))
|
||||
|
||||
(defmacro defmethod [mm dispatch-val & fn-tail]
|
||||
`(defmethod-setup (quote ~mm) ~dispatch-val (fn ~@fn-tail)))
|
||||
;; the expansion ns rides along so a deferred defmethod resolves its multifn
|
||||
;; against the ns it was written in (aliases and refers included).
|
||||
`(defmethod-setup (quote ~mm) ~dispatch-val (fn ~@fn-tail)
|
||||
~(str (clojure.core/ns-name clojure.core/*ns*))))
|
||||
|
||||
;; Multimethod table ops: a multimethod's method table lives on its
|
||||
;; VAR (the value is just the dispatch closure), so these pass the name quoted
|
||||
|
|
|
|||
|
|
@ -6,28 +6,32 @@
|
|||
(if (nil? s) true
|
||||
(= 0 (count (str-trim s)))))
|
||||
|
||||
;; The case fns and the searches take any Object s through its toString, like
|
||||
;; the reference ((upper-case :kw) is ":KW", (capitalize 1) is "1"); nil throws
|
||||
;; like calling a method on null.
|
||||
(defn- to-str [s]
|
||||
(if (nil? s)
|
||||
(throw (new NullPointerException "s"))
|
||||
(.toString s)))
|
||||
(defn capitalize
|
||||
|
||||
[s]
|
||||
(let [s (to-str s)]
|
||||
(if (< 1 (count s))
|
||||
(str (str-upper (subs s 0 1))
|
||||
(str-lower (subs s 1)))
|
||||
(str-upper s)))
|
||||
(str-upper s))))
|
||||
|
||||
(defn lower-case
|
||||
|
||||
[s]
|
||||
(str-lower s))
|
||||
(str-lower (to-str s)))
|
||||
|
||||
(defn upper-case
|
||||
|
||||
[s]
|
||||
(str-upper s))
|
||||
(str-upper (to-str s)))
|
||||
|
||||
(defn includes?
|
||||
|
||||
[s substr]
|
||||
(not (nil? (str-find substr s))))
|
||||
(not (nil? (str-find substr (to-str s)))))
|
||||
|
||||
(defn join
|
||||
|
||||
|
|
@ -36,11 +40,11 @@
|
|||
|
||||
(defn replace
|
||||
[s match replacement]
|
||||
(str-replace-all match replacement s))
|
||||
(str-replace-all match replacement (to-str s)))
|
||||
|
||||
(defn replace-first
|
||||
[s match replacement]
|
||||
(str-replace match replacement s))
|
||||
(str-replace match replacement (to-str s)))
|
||||
|
||||
(defn reverse
|
||||
[s]
|
||||
|
|
@ -68,16 +72,18 @@
|
|||
(vec (str-split #"\r?\n" s)))
|
||||
|
||||
(defn starts-with?
|
||||
|
||||
[s substr]
|
||||
(let [slen (count s) slen2 (count substr)]
|
||||
(when (nil? substr) (throw (new NullPointerException "substr")))
|
||||
(let [s (to-str s)
|
||||
slen (count s) slen2 (count substr)]
|
||||
(and (>= slen slen2)
|
||||
(= (subs s 0 slen2) substr))))
|
||||
|
||||
(defn ends-with?
|
||||
|
||||
[s substr]
|
||||
(let [slen (count s) slen2 (count substr)]
|
||||
(when (nil? substr) (throw (new NullPointerException "substr")))
|
||||
(let [s (to-str s)
|
||||
slen (count s) slen2 (count substr)]
|
||||
(and (>= slen slen2)
|
||||
(= (subs s (- slen slen2)) substr))))
|
||||
|
||||
|
|
@ -97,8 +103,8 @@
|
|||
(str-trimr s))
|
||||
|
||||
(defn escape
|
||||
|
||||
[s cmap]
|
||||
(when (nil? s) (throw (new NullPointerException "s")))
|
||||
(apply str
|
||||
(map (fn [ch]
|
||||
(if-let [rep (cmap ch)] rep (str ch)))
|
||||
|
|
@ -107,9 +113,9 @@
|
|||
(defn index-of
|
||||
"0-based index of the first occurrence of value in s, or nil."
|
||||
([s value]
|
||||
(str-find value s))
|
||||
(str-find value (to-str s)))
|
||||
([s value from]
|
||||
(let [idx (str-find value (subs s from))]
|
||||
(let [idx (str-find value (subs (to-str s) from))]
|
||||
(when idx (+ from idx)))))
|
||||
|
||||
(defn last-index-of
|
||||
|
|
|
|||
|
|
@ -3558,4 +3558,11 @@
|
|||
{:suite "casts / checked narrow" :label "float range-checks against Float/MAX_VALUE" :expected "[:iae 1.5]" :actual "[(try (float 1e300) (catch IllegalArgumentException _ :iae)) (float 1.5)]"}
|
||||
{:suite "casts / unchecked wrap" :label "unchecked casts wrap and sign-fold; doubles saturate" :expected "[-56 -25536 \\A 9223372036854775807 0 0]" :actual "[(unchecked-byte 200) (unchecked-short 40000) (unchecked-char 65) (unchecked-long 1e300) (unchecked-int 4294967296) (unchecked-long ##NaN)]"}
|
||||
{:suite "ns / requiring-resolve" :label "loads the namespace then resolves; unqualified throws" :expected "[#{1 2} :iae]" :actual "[((requiring-resolve (quote clojure.set/union)) #{1} #{2}) (try (requiring-resolve (quote unqualified)) (catch IllegalArgumentException _ :iae))]"}
|
||||
{:suite "string / toString coercion" :label "case fns and searches take any Object through toString" :expected "[\":ASDF/X\" \"asdf\" \"1\" true true]" :actual "[(clojure.string/upper-case :asdf/x) (clojure.string/lower-case (quote ASDF)) (clojure.string/capitalize 1) (clojure.string/starts-with? :ab \":a\") (clojure.string/ends-with? (quote ab) \"b\")]"}
|
||||
{:suite "string / toString coercion" :label "nil s throws; nil substr throws" :expected "[:t :t :t]" :actual "[(try (clojure.string/upper-case nil) (catch Throwable _ :t)) (try (clojure.string/starts-with? \"\" nil) (catch Throwable _ :t)) (try (clojure.string/escape nil {}) (catch Throwable _ :t))]"}
|
||||
{:suite "core / some-fn" :label "returns the last predicate value (false, not nil); zero preds is an arity error" :expected "[false 2 :ae]" :actual "[((some-fn even?) 1) ((some-fn :a :b) {:b 2}) (try (some-fn) (catch clojure.lang.ArityException _ :ae))]"}
|
||||
{:suite "core / ifn?" :label "multimethods and promises are IFn; promises deliver when invoked" :expected "[true true 7]" :actual "[(do (defmulti ifnq-mm identity) (ifn? ifnq-mm)) (ifn? (promise)) (let [p (promise)] (p 7) (deref p))]"}
|
||||
{:suite "multimethods / deferred definition" :label "defmulti/defmethod inside a fn intern in the ns they were written in" :expected ":hit" :actual "((fn [] (defmulti cdm-deferred first) (defmethod cdm-deferred :a [_] :hit) (cdm-deferred [:a 1])))"}
|
||||
{:suite "host-interop / wrapper ctors and TYPE" :label "Boolean/Integer/Double ctors; primitive TYPE tokens" :expected "[false true 12 1.5 \"int\" \"double\"]" :actual "[(Boolean. \"yes\") (Boolean. \"TRUE\") (Integer. \"12\") (Double. \"1.5\") (str Integer/TYPE) (str Double/TYPE)]"}
|
||||
{:suite "host-interop / IReduce" :label ".reduce drives a collection's own reduce" :expected "[7 6]" :actual "[(.reduce [1 2 3] + 1) (.reduce [1 2 3] +)]"}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ clojure.core-test.abs 1 0
|
|||
clojure.core-test.add-watch 0 1
|
||||
clojure.core-test.bigint 6 0
|
||||
clojure.core-test.bit-set 1 0
|
||||
clojure.core-test.boolean-qmark 0 3
|
||||
clojure.core-test.compare 1 0
|
||||
clojure.core-test.conj 1 0
|
||||
clojure.core-test.contains-qmark 3 0
|
||||
|
|
@ -19,7 +18,6 @@ clojure.core-test.eval 0 3
|
|||
clojure.core-test.even-qmark 1 0
|
||||
clojure.core-test.float 1 0
|
||||
clojure.core-test.get 0 1
|
||||
clojure.core-test.ifn-qmark 2 0
|
||||
clojure.core-test.inc 1 0
|
||||
clojure.core-test.int-qmark 3 0
|
||||
clojure.core-test.intern 2 0
|
||||
|
|
@ -44,14 +42,12 @@ clojure.core-test.quot 30 0
|
|||
clojure.core-test.rand-nth 0 1
|
||||
clojure.core-test.rational-qmark 3 0
|
||||
clojure.core-test.realized-qmark 3 0
|
||||
clojure.core-test.reduce 0 1
|
||||
clojure.core-test.rem 21 0
|
||||
clojure.core-test.remove-watch 0 1
|
||||
clojure.core-test.run-bang 1 0
|
||||
clojure.core-test.select-keys 2 0
|
||||
clojure.core-test.seqable-qmark 1 0
|
||||
clojure.core-test.shuffle 1 0
|
||||
clojure.core-test.some-fn 3 0
|
||||
clojure.core-test.sort-by 2 0
|
||||
clojure.core-test.special-symbol-qmark 4 0
|
||||
clojure.core-test.star 13 0
|
||||
|
|
@ -62,9 +58,3 @@ clojure.core-test.vals 0 3
|
|||
clojure.core-test.vec 1 0
|
||||
clojure.core-test.when-let 1 0
|
||||
clojure.edn-test.read-string 46 5
|
||||
clojure.string-test.capitalize 0 4
|
||||
clojure.string-test.ends-with-qmark 1 4
|
||||
clojure.string-test.escape 1 0
|
||||
clojure.string-test.lower-case 0 4
|
||||
clojure.string-test.starts-with-qmark 1 10
|
||||
clojure.string-test.upper-case 0 4
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue