Skip to content

Commit

Permalink
Add missing clojure.core functions (#328)
Browse files Browse the repository at this point in the history
add time, parse-long, parse-double, and parse-boolean functions
  • Loading branch information
TiLogic authored Nov 26, 2024
1 parent 97a3ba2 commit 6f76cef
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 1 deletion.
45 changes: 45 additions & 0 deletions clj/src/cljd/core.cljd
Original file line number Diff line number Diff line change
Expand Up @@ -8760,3 +8760,48 @@ specified.
([aseq]
(.from #/(List dynamic) aseq .growable false))
([type aseq] (into-array aseq)))

(defmacro time
"Evaluates expr and prints the time it took. Returns the value of expr."
{:added "1.0"}
[expr]
`(let [stopwatch# (dart:core/Stopwatch)
_# (.start stopwatch#)
ret# ~expr]
(.stop stopwatch#)
(prn (str "Elapsed time: " (/ (.-elapsedMicroseconds stopwatch#) 1000) " msecs"))
ret#))

(defn- parsing-err
"Construct message for parsing for non-string parsing error"
[val]
(str "Expected string, got " (if (nil? val) "nil" (.-runtimeType val))))

(defn ^int? parse-long
{:doc "Parse string of decimal digits with optional leading -/+ and return a
Long value, or nil if parse fails"
:added "1.11"}
[s]
(if (string? s)
(int/tryParse s)
(throw (ArgumentError (parsing-err s)))))

(defn ^double? parse-double
{:doc "Parse string with floating point components and return a Double value,
or nil if parse fails."
:added "1.11"}
[s]
(if (string? s)
(double/tryParse s)
(throw (ArgumentError (parsing-err s)))))

(defn ^bool? parse-boolean
{:doc "Parse strings \"true\" or \"false\" and return a boolean, or nil if invalid"
:added "1.11"}
[s]
(if (string? s)
(case s
"true" true
"false" false
nil)
(throw (ArgumentError (parsing-err s)))))
105 changes: 105 additions & 0 deletions clj/test/cljd/test_clojure/parse.cljd
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
(ns cljd.test-clojure.parse
(:require
[clojure.test :refer [deftest is are]]))

(def max-int-32 0x7fffffff) ;; 2147483647
(def max-int-64 0x7fffffffffffffff) ;; 9223372036854775807

(defn int-32->int64 [n]
(+ n (- max-int-64 max-int-32)))

(deftest test-parse-long
(are [s expected]
(= expected (parse-long s))
"100" 100
"+100" 100
"0" 0
"+0" 0
"-0" 0
"-42" -42
"9223372036854775807" 9223372036854775807
"+9223372036854775807" +9223372036854775807
"-9223372036854775808" -9223372036854775808
"0xA0" 160 ;; hex (clj(s) do not support hex)
"077" 77)

(are [s] ;; do not parse
(nil? (parse-long s))
"0.3" ;; no float
"9223372036854775808" ;; past max
"-9223372036854775809" ;; past min
"2r010");; no radix support

(are [s] ;; do not parse
(nil? (parse-long s))
"9223372036854775808" ;; past max
"-9223372036854775809" ;; past min
))

;; generative test - gen long -> str -> parse, compare
(deftest test-gen-parse-long
(let [res (reduce (fn [_ _]
(let [f (comp (rand-nth [identity int-32->int64]) (rand-nth [identity -]))
n (-> max-int-32 rand-int f)
r (= n (-> n str parse-long))]
(if r
r
(reduced n))))
false
(range 100000))]

(is (= max-int-64 (int-32->int64 max-int-32)))
(is true res)))

(deftest test-parse-double
(are [s expected]
(= expected (parse-double s))
"1.234" 1.234
"+1.234" 1.234
"-1.234" -1.234
"+0" +0.0
"-0.0" -0.0
"0.0" 0.0
"5" 5.0
"Infinity" dart:core/double.infinity
"-Infinity" dart:core/double.negativeInfinity
"1.7976931348623157E308" dart:core/double.maxFinite
"4.9E-324" dart:core/double.minPositive
"1.7976931348623157E309" dart:core/double.infinity ;; past max double
"2.5e-324" dart:core/double.minPositive ;; past min double, above half minimum
"2.4e-324" 0.0) ;; below minimum double
(is (dart:core/double.nan (parse-double "NaN")))
(are [s] ;; nil on invalid string
(nil? (parse-double s))
"double" ;; invalid string
"1.7976931348623157G309")) ;; invalid, but similar to valid

;; generative test - gen double -> str -> parse, compare
(deftest test-gen-parse-double
(let [res (reduce (fn [_ _]
(let [f (rand-nth [identity -])
n (-> 1.7976931348623157E308 rand f)
r (= n (-> n str parse-double))]
(if r
r
(reduced n))))
false
(range 100000))]

(is true res)))

(deftest test-parse-boolean
(is (identical? true (parse-boolean "true")))
(is (identical? false (parse-boolean "false")))

(are [s] ;; nil on invalid string
(nil? (parse-boolean s))
"abc"
"TRUE"
"FALSE"
" true ")

(is (thrown? ArgumentError (parse-boolean nil)))
(is (thrown? ArgumentError (parse-boolean false)))
(is (thrown? ArgumentError (parse-boolean true)))
(is (thrown? ArgumentError (parse-boolean 100))))
2 changes: 1 addition & 1 deletion run-tests
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ EOF
cd tmp/tests
clojure -M -m cljd.build init
dart pub add -d test || true
clojure -M -m cljd.build compile cljd.test-clojure.core-test-cljd cljd.test-clojure.string cljd.test-clojure.core-test cljd.test-clojure.for cljd.test-clojure.clojure-zip cljd.test-clojure.clojure-set cljd.test-clojure.clojure-walk cljd.test-clojure.other-functions cljd.test-clojure.test-test cljd.test-reader.reader-test cljd.test-clojure.primitives-test cljd.test-clojure.data
clojure -M -m cljd.build compile cljd.test-clojure.core-test-cljd cljd.test-clojure.string cljd.test-clojure.core-test cljd.test-clojure.for cljd.test-clojure.clojure-zip cljd.test-clojure.clojure-set cljd.test-clojure.clojure-walk cljd.test-clojure.other-functions cljd.test-clojure.test-test cljd.test-reader.reader-test cljd.test-clojure.primitives-test cljd.test-clojure.data cljd.test-clojure.parse
# --enable-experiment=records is there for making tests pass even on dart2
# on dart3 it should be a no op.
dart --enable-experiment=records test -p vm

0 comments on commit 6f76cef

Please sign in to comment.