From d5a504f7146d6856eea83c9cab48caf8d070a159 Mon Sep 17 00:00:00 2001 From: loicb Date: Mon, 7 Sep 2026 16:14:31 +0800 Subject: [PATCH 1/2] fix(stdlib): match JVM behaviour in clojure.string/split (#174) .NET's Regex.Split has none of Java's three split rules, so they are applied here. Java drops trailing empty strings at limit 0. It returns every part at a negative limit. It also drops a leading empty part when the pattern matches nothing at the start. --- CHANGELOG.md | 1 + magic-compiler/src/stdlib/clojure/string.clj | 23 +++++++++++++++---- magic-compiler/test/magic/test/string.clj | 24 ++++++++++++++++++-- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 812720f43..7abe8e059 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - A namespace map prints its keys in the map's own order, so `#:a{...}` no longer reorders them once the map outgrows an array-map - [#165](https://github.com/flybot-sg/magic/issues/165). - `clojure.pprint/pprint` writes collection metadata when `*print-meta*` is true, so a pretty-printed value carries its metadata like `pr` does - [#166](https://github.com/flybot-sg/magic/issues/166). - `clojure.repl/doc` prints a special form's docstring once instead of repeating it after the "Please see" line - [#167](https://github.com/flybot-sg/magic/issues/167). +- `clojure.string/split` drops trailing empty strings, and a negative limit returns every part. `(split "a b " #" ")` returned `["a" "b" ""]`. `split-lines` gained a final `""` on a trailing newline, and the negative limit threw. A pattern that matches nothing at the start no longer adds a leading `""`, so `(split "abc" #"")` returns `["a" "b" "c"]` - [#174](https://github.com/flybot-sg/magic/issues/174). ### Mage - `il/type`'s short arities work, so a caller can write `(il/type "Name" body)` instead of spelling out attributes, interfaces, supertype, generic parameters and custom attributes every time. Every arity below the 7-arity threw `ArityException` - [#143](https://github.com/flybot-sg/magic/issues/143). diff --git a/magic-compiler/src/stdlib/clojure/string.clj b/magic-compiler/src/stdlib/clojure/string.clj index 0032683ff..e5136c0a7 100644 --- a/magic-compiler/src/stdlib/clojure/string.clj +++ b/magic-compiler/src/stdlib/clojure/string.clj @@ -218,15 +218,30 @@ Design notes for clojure.string: (defn split "Splits string on a regular expression. Optional argument limit is - the maximum number of splits. Not lazy. Returns vector of the splits." + the maximum number of splits. Not lazy. Returns vector of the splits. + Trailing empty strings are not returned - pass limit of -1 to return all." {:added "1.2"} ([^String s ^Regex re] ;;; ^Pattern - (LazilyPersistentVector/createOwning (.Split re s))) ;;; .split + (split s re 0)) ([^String s ^Regex re limit] ;;; ^Pattern - (LazilyPersistentVector/createOwning (.Split re s limit)))) ;;; .split + ;; Java's limit rules and its zero-width-at-0 rule are applied here, + ;; because .NET's Regex.Split has none of them. + (if (pos? limit) + (LazilyPersistentVector/createOwning (.Split re s limit)) ;;; .split + (let [m (.Match re s) + split (LazilyPersistentVector/createOwning (.Split re s)) ;;; .split + parts (if (and (.Success m) (zero? (.Index m)) (zero? (.Length m))) + (subvec split 1) + split)] + (if (or (neg? limit) (= 1 (count parts))) + parts + (loop [parts parts] + (if (and (seq parts) (= "" (peek parts))) + (recur (pop parts)) + parts))))))) (defn split-lines - "Splits s on \\n or \\r\\n." + "Splits s on \\n or \\r\\n. Trailing empty lines are not returned." {:added "1.2"} [^String s] (split s #"\r?\n")) diff --git a/magic-compiler/test/magic/test/string.clj b/magic-compiler/test/magic/test/string.clj index 624593522..3ef70ad87 100644 --- a/magic-compiler/test/magic/test/string.clj +++ b/magic-compiler/test/magic/test/string.clj @@ -5,7 +5,20 @@ (deftest t-split (is (= ["a" "b"] (clojure.string/split "a-b" #"-"))) (is (= ["a" "b-c"] (clojure.string/split "a-b-c" #"-" 2))) - (is (vector? (clojure.string/split "abc" #"-")))) + (is (vector? (clojure.string/split "abc" #"-"))) + (is (= ["a" "b"] (clojure.string/split "a b " #" "))) + (is (= ["a"] (clojure.string/split "a,," #","))) + (is (= [] (clojure.string/split "," #","))) + (is (= [""] (clojure.string/split "" #","))) + (is (= ["a" "" "b"] (clojure.string/split "a,,b" #","))) + (is (= ["a" "b"] (clojure.string/split "a b " #" " 0))) + (is (= ["a" "b" ""] (clojure.string/split "a b " #" " -1))) + (is (= ["a" "b" "c"] (clojure.string/split "abc" #""))) + (is (= ["a" "b" "c" ""] (clojure.string/split "abc" #"" -1))) + (is (= [""] (clojure.string/split "" #""))) + (is (= ["" "a"] (clojure.string/split ",a" #","))) + (is (= ["" "" "a"] (clojure.string/split "XaX" #"X*"))) + (is (= ["a" "" "b" "" "c"] (clojure.string/split "aXbXc" #"X*")))) (deftest t-reverse (is (= "tab" (clojure.string/reverse "bat")))) @@ -103,7 +116,14 @@ (deftest t-split-lines (is (= ["one" "two" "three"] (clojure.string/split-lines "one\ntwo\r\nthree"))) (is (vector? (clojure.string/split-lines "one\ntwo\r\nthree"))) - (is (= (list "foo") (clojure.string/split-lines "foo")))) + (is (= (list "foo") (clojure.string/split-lines "foo"))) + (is (= ["a" "b"] (clojure.string/split-lines "a\nb\n"))) + (is (= ["a" "b"] (clojure.string/split-lines "a\r\nb\r\n"))) + (is (= [""] (clojure.string/split-lines ""))) + (is (= [] (clojure.string/split-lines "\n"))) + (is (= [] (clojure.string/split-lines "\n\n"))) + (is (= ["foo"] (clojure.string/split-lines "foo\n\n"))) + (is (= ["" "bar"] (clojure.string/split-lines "\nbar")))) (deftest t-index-of (is (let [sb "tacos"] (= 2 (clojure.string/index-of sb "c")))) From 0b2548226ab6aceb2ed5b09169baec27131769a2 Mon Sep 17 00:00:00 2001 From: loicb Date: Mon, 7 Sep 2026 16:14:31 +0800 Subject: [PATCH 2/2] chore(bootstrap): refresh clojure.string DLL for the split fix (#174) --- magic-compiler/dll-sources.edn | 2 +- .../Runtime/magic/clojure.string.clj.dll | Bin 42496 -> 43520 bytes nostrand/references/clojure.string.clj.dll | Bin 42496 -> 43520 bytes 3 files changed, 1 insertion(+), 1 deletion(-) diff --git a/magic-compiler/dll-sources.edn b/magic-compiler/dll-sources.edn index 9a8bdd2cb..cc73b6681 100644 --- a/magic-compiler/dll-sources.edn +++ b/magic-compiler/dll-sources.edn @@ -35,7 +35,7 @@ clojure.spec.alpha {:source "magic-compiler/src/stdlib/clojure/spec/alpha.clj", :sha256 "8ba9e4e0d5826e64734cb58f986a680b9a09d328453c37db0f454cfb4a41ab90"} clojure.spec.gen.alpha {:source "magic-compiler/src/stdlib/clojure/spec/gen/alpha.clj", :sha256 "7529d23186c7622592fce537e119efb914dc54398bdbef45209a6a24ed54ebc4"} clojure.stacktrace {:source "magic-compiler/src/stdlib/clojure/stacktrace.clj", :sha256 "a0065d213e7d7931130956a3c098392d2be0b91b9537b5f13c97bb670beb6b9c"} - clojure.string {:source "magic-compiler/src/stdlib/clojure/string.clj", :sha256 "c34346ea918e1d316d99a178c4f3225a8b0f9a4ff4a6b6f72793807556275e12"} + clojure.string {:source "magic-compiler/src/stdlib/clojure/string.clj", :sha256 "4a1288d7608febb5cf109df44b7d6148cf4308654077e01cb7aab5abbad8612a"} clojure.template {:source "magic-compiler/src/stdlib/clojure/template.clj", :sha256 "b31d7b0bbcab1f44f8970d187aa193640069ca14301daa3d57e8ab47dfeccebb"} clojure.test {:source "magic-compiler/src/stdlib/clojure/test.clj", :sha256 "12f173c4a3c79a0ed81d7b7958f57ba7a8a70441f3d415bd67d198087d820682"} clojure.uuid {:source "magic-compiler/src/stdlib/clojure/uuid.clj", :sha256 "04bb8e17c967f2998796b1c313b9503198b7388956dd496bd6a4a5b168487009"} diff --git a/magic-unity/Runtime/magic/clojure.string.clj.dll b/magic-unity/Runtime/magic/clojure.string.clj.dll index 1f88a259bd3f2999645d7bab780df75924f55237..fc729783a9415e68b0024622527b217ef1bb363c 100755 GIT binary patch delta 8006 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 CcUR>A delta 6938 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 CcUR>A delta 6938 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