diff --git a/src/Data/String.js b/src/Data/String.js index e601fbf..1c46b9a 100644 --- a/src/Data/String.js +++ b/src/Data/String.js @@ -153,15 +153,9 @@ exports.split = function (sep) { }; }; -exports._splitAt = function (just) { - return function (nothing) { - return function (i) { - return function (s) { - return i >= 0 && i < s.length ? - just({ before: s.substring(0, i), after: s.substring(i) }) : - nothing; - }; - }; +exports.splitAt = function (i) { + return function (s) { + return { before: s.substring(0, i), after: s.substring(i) }; }; }; diff --git a/src/Data/String.purs b/src/Data/String.purs index b6625b5..c377316 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -421,21 +421,26 @@ foreign import count :: (Char -> Boolean) -> String -> Int -- | foreign import split :: Pattern -> String -> Array String --- | Returns the substrings of a split at the given index, if the index is within bounds. +-- | Splits a string into two substrings, where `before` contains the +-- | characters up to (but not including) the given index, and `after` contains +-- | the rest of the string, from that index on. -- | -- | ```purescript --- | splitAt 2 "Hello World" == Just { before: "He", after: "llo World"} --- | splitAt 10 "Hi" == Nothing +-- | splitAt 2 "Hello World" == { before: "He", after: "llo World"} +-- | splitAt 10 "Hi" == { before: "Hi", after: ""} -- | ``` -- | -splitAt :: Int -> String -> Maybe { before :: String, after :: String } -splitAt = _splitAt Just Nothing - -foreign import _splitAt :: (forall a. a -> Maybe a) - -> (forall a. Maybe a) - -> Int - -> String - -> Maybe { before :: String, after :: String } +-- | Thus the length of `(splitAt i s).before` will equal either `i` or +-- | `length s`, if that is shorter. (Or if `i` is negative the length will be +-- | 0.) +-- | +-- | In code: +-- | ```purescript +-- | length (splitAt i s).before == min (max i 0) (length s) +-- | (splitAt i s).before <> (splitAt i s).after == s +-- | splitAt i s == {before: take i s, after: drop i s} +-- | ``` +foreign import splitAt :: Int -> String -> { before :: String, after :: String } -- | Converts the string into an array of characters. -- | diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 8364670..a48a764 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -340,25 +340,32 @@ singletonFallback (CodePoint cp) = fromCharCode lead <> fromCharCode trail --- | Returns a record with strings created from the code points on either side --- | of the given index. If the index is not within the string, Nothing is --- | returned. +-- | Splits a string into two substrings, where `before` contains the code +-- | points up to (but not including) the given index, and `after` contains the +-- | rest of the string, from that index on. -- | -- | ```purescript -- | >>> splitAt 3 "b 𝐀𝐀 c 𝐀" -- | Just { before: "b 𝐀", after: "𝐀 c 𝐀" } -- | ``` -- | -splitAt :: Int -> String -> Maybe { before :: String, after :: String } +-- | Thus the length of `(splitAt i s).before` will equal either `i` or +-- | `length s`, if that is shorter. (Or if `i` is negative the length will be +-- | 0.) +-- | +-- | In code: +-- | ```purescript +-- | length (splitAt i s).before == min (max i 0) (length s) +-- | (splitAt i s).before <> (splitAt i s).after == s +-- | splitAt i s == {before: take i s, after: drop i s} +-- | ``` +splitAt :: Int -> String -> { before :: String, after :: String } splitAt i s = - let cps = toCodePointArray s in - if i < 0 || Array.length cps < i - then Nothing - else Just { - before: fromCodePointArray (Array.take i cps), - after: fromCodePointArray (Array.drop i cps) - } - + let before = take i s in + { before + -- inline drop i s to reuse the result of take i s + , after: String.drop (String.length before) s + } -- | Returns a string containing the given number of code points from the -- | beginning of the given string. If the string does not have that many code diff --git a/src/Data/String/NonEmpty.purs b/src/Data/String/NonEmpty.purs index b3d93a9..59306d3 100644 --- a/src/Data/String/NonEmpty.purs +++ b/src/Data/String/NonEmpty.purs @@ -412,12 +412,10 @@ count = liftS <<< String.count splitAt :: Int -> NonEmptyString - -> Maybe { before :: Maybe NonEmptyString, after :: Maybe NonEmptyString } -splitAt i (NonEmptyString s) = case String.splitAt i s of - Just { before, after } -> - Just { before: fromString before, after: fromString after } - Nothing -> - Nothing + -> { before :: Maybe NonEmptyString, after :: Maybe NonEmptyString } +splitAt i (NonEmptyString s) = + case String.splitAt i s of + { before, after } -> { before: fromString before, after: fromString after } -- | Returns the argument converted to lowercase. -- | diff --git a/src/Data/String/Regex.js b/src/Data/String/Regex.js index 2e3ac3b..0b84c5b 100644 --- a/src/Data/String/Regex.js +++ b/src/Data/String/Regex.js @@ -46,7 +46,7 @@ exports._match = function (just) { return function (r) { return function (s) { var m = s.match(r); - if (m == null) { + if (m == null || m.length === 0) { return nothing; } else { for (var i = 0; i < m.length; i++) { diff --git a/src/Data/String/Regex.purs b/src/Data/String/Regex.purs index d60b9ee..79a1a9f 100644 --- a/src/Data/String/Regex.purs +++ b/src/Data/String/Regex.purs @@ -18,6 +18,7 @@ module Data.String.Regex import Prelude +import Data.Array.NonEmpty (NonEmptyArray) import Data.Either (Either(..)) import Data.Maybe (Maybe(..)) import Data.String (Pattern(..), contains) @@ -82,13 +83,13 @@ foreign import _match -> (forall r. Maybe r) -> Regex -> String - -> Maybe (Array (Maybe String)) + -> Maybe (NonEmptyArray (Maybe String)) -- | Matches the string against the `Regex` and returns an array of matches -- | if there were any. Each match has type `Maybe String`, where `Nothing` -- | represents an unmatched optional capturing group. -- | See [reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match). -match :: Regex -> String -> Maybe (Array (Maybe String)) +match :: Regex -> String -> Maybe (NonEmptyArray (Maybe String)) match = _match Just Nothing -- | Replaces occurences of the `Regex` with the first string. The replacement diff --git a/test/Test/Data/String.purs b/test/Test/Data/String.purs index 24f0a4f..a99fbbe 100644 --- a/test/Test/Data/String.purs +++ b/test/Test/Data/String.purs @@ -5,7 +5,7 @@ import Prelude (Unit, Ordering(..), (==), ($), discard, negate, not, (/=), (&&)) import Effect (Effect) import Effect.Console (log) -import Data.Maybe (Maybe(..), isNothing, maybe) +import Data.Maybe (Maybe(..), isNothing) import Data.String import Test.Assert (assert) @@ -174,19 +174,18 @@ testString = do assert $ split (Pattern "d") "abc" == ["abc"] log "splitAt" - let testSplitAt i str res = + let testSplitAt i str r = assert $ case splitAt i str of - Nothing -> - isNothing res - Just { before, after } -> - maybe false (\r -> - r.before == before && r.after == after) res - - testSplitAt 1 "" Nothing - testSplitAt 0 "a" $ Just {before: "", after: "a"} - testSplitAt 1 "ab" $ Just {before: "a", after: "b"} - testSplitAt 3 "aabcc" $ Just {before: "aab", after: "cc"} - testSplitAt (-1) "abc" $ Nothing + { before, after } -> + r.before == before && r.after == after + + testSplitAt 1 "" {before: "", after: ""} + testSplitAt 0 "a" {before: "", after: "a"} + testSplitAt 1 "a" {before: "a", after: ""} + testSplitAt 1 "ab" {before: "a", after: "b"} + testSplitAt 3 "aabcc" {before: "aab", after: "cc"} + testSplitAt (-1) "abc" {before: "", after: "abc"} + testSplitAt 10 "Hi" {before: "Hi", after: ""} log "toCharArray" assert $ toCharArray "" == [] diff --git a/test/Test/Data/String/CodePoints.purs b/test/Test/Data/String/CodePoints.purs index 56ba501..7d7a8cc 100644 --- a/test/Test/Data/String/CodePoints.purs +++ b/test/Test/Data/String/CodePoints.purs @@ -152,29 +152,26 @@ testStringCodePoints = do assert $ (singleton <$> codePointFromInt 0x16805) == Just "\x16805" log "splitAt" - let testSplitAt i s res = + let testSplitAt i s r = assert $ case splitAt i s of - Nothing -> - isNothing res - Just { before, after } -> - maybe false (\r -> - r.before == before && r.after == after) res - - testSplitAt 0 "" $ Just {before: "", after: ""} - testSplitAt 1 "" Nothing - testSplitAt 0 "a" $ Just {before: "", after: "a"} - testSplitAt 1 "ab" $ Just {before: "a", after: "b"} - testSplitAt 3 "aabcc" $ Just {before: "aab", after: "cc"} - testSplitAt (-1) "abc" $ Nothing - testSplitAt 0 str $ Just {before: "", after: str} - testSplitAt 1 str $ Just {before: "a", after: "\xDC00\xD800\xD800\x16805\x16A06\&z"} - testSplitAt 2 str $ Just {before: "a\xDC00", after: "\xD800\xD800\x16805\x16A06\&z"} - testSplitAt 3 str $ Just {before: "a\xDC00\xD800", after: "\xD800\x16805\x16A06\&z"} - testSplitAt 4 str $ Just {before: "a\xDC00\xD800\xD800", after: "\x16805\x16A06\&z"} - testSplitAt 5 str $ Just {before: "a\xDC00\xD800\xD800\x16805", after: "\x16A06\&z"} - testSplitAt 6 str $ Just {before: "a\xDC00\xD800\xD800\x16805\x16A06", after: "z"} - testSplitAt 7 str $ Just {before: str, after: ""} - testSplitAt 8 str $ Nothing + { before, after } -> + r.before == before && r.after == after + + testSplitAt 0 "" {before: "", after: "" } + testSplitAt 1 "" {before: "", after: "" } + testSplitAt 0 "a" {before: "", after: "a"} + testSplitAt 1 "ab" {before: "a", after: "b"} + testSplitAt 3 "aabcc" {before: "aab", after: "cc"} + testSplitAt (-1) "abc" {before: "", after: "abc"} + testSplitAt 0 str {before: "", after: str} + testSplitAt 1 str {before: "a", after: "\xDC00\xD800\xD800\x16805\x16A06\&z"} + testSplitAt 2 str {before: "a\xDC00", after: "\xD800\xD800\x16805\x16A06\&z"} + testSplitAt 3 str {before: "a\xDC00\xD800", after: "\xD800\x16805\x16A06\&z"} + testSplitAt 4 str {before: "a\xDC00\xD800\xD800", after: "\x16805\x16A06\&z"} + testSplitAt 5 str {before: "a\xDC00\xD800\xD800\x16805", after: "\x16A06\&z"} + testSplitAt 6 str {before: "a\xDC00\xD800\xD800\x16805\x16A06", after: "z"} + testSplitAt 7 str {before: str, after: ""} + testSplitAt 8 str {before: str, after: ""} log "take" assert $ take (-1) str == "" diff --git a/test/Test/Data/String/NonEmpty.purs b/test/Test/Data/String/NonEmpty.purs index e9cd771..366ff0b 100644 --- a/test/Test/Data/String/NonEmpty.purs +++ b/test/Test/Data/String/NonEmpty.purs @@ -203,15 +203,11 @@ testNonEmptyString = do let testSplitAt i str res = assert $ case splitAt i str of - Nothing -> - isNothing res - Just { before, after } -> - maybe false (\r -> - r.before == before && r.after == after) res - testSplitAt 0 (nes "a") (Just { before: Nothing, after: Just (nes "a") }) - testSplitAt 1 (nes "ab") (Just { before: Just (nes "a"), after: Just (nes "b") }) - testSplitAt 3 (nes "aabcc") (Just { before: Just (nes "aab"), after: Just (nes "cc") }) - testSplitAt (-1) (nes "abc") Nothing + { before, after } -> res.before == before && res.after == after + testSplitAt 0 (nes "a") { before: Nothing, after: Just (nes "a") } + testSplitAt 1 (nes "ab") { before: Just (nes "a"), after: Just (nes "b") } + testSplitAt 3 (nes "aabcc") { before: Just (nes "aab"), after: Just (nes "cc") } + testSplitAt (-1) (nes "abc") { before: Nothing, after: Just (nes "abc") } log "toLower" assert $ toLower (nes "bAtMaN") == nes "batman" diff --git a/test/Test/Data/String/Regex.purs b/test/Test/Data/String/Regex.purs index 0faaae2..326b35e 100644 --- a/test/Test/Data/String/Regex.purs +++ b/test/Test/Data/String/Regex.purs @@ -1,16 +1,16 @@ module Test.Data.String.Regex (testStringRegex) where -import Prelude (Unit, ($), (<>), discard, (==), not) - -import Effect (Effect) -import Effect.Console (log) +import Data.String.Regex +import Data.Array.NonEmpty (NonEmptyArray, fromArray) import Data.Either (isLeft) -import Data.Maybe (Maybe(..)) -import Data.String.Regex +import Data.Maybe (Maybe(..), fromJust) import Data.String.Regex.Flags (global, ignoreCase, noFlags) import Data.String.Regex.Unsafe (unsafeRegex) - +import Effect (Effect) +import Effect.Console (log) +import Partial.Unsafe (unsafePartial) +import Prelude (type (~>), Unit, discard, not, ($), (<<<), (<>), (==)) import Test.Assert (assert) testStringRegex :: Effect Unit @@ -26,7 +26,8 @@ testStringRegex = do assert $ "quxbarquxbaz" == replace (unsafeRegex "foo" (global <> ignoreCase)) "qux" "foobarFOObaz" log "match" - assert $ match (unsafeRegex "^abc$" noFlags) "abc" == Just [Just "abc"] + assert $ match (unsafeRegex "^abc$" noFlags) "abc" == Just (nea [Just "abc"]) + assert $ match (unsafeRegex "^abc$" noFlags) "xyz" == Nothing log "replace" assert $ replace (unsafeRegex "-" noFlags) "!" "a-b-c" == "a!b-c" @@ -50,3 +51,6 @@ testStringRegex = do let pattern = unsafeRegex "a" (parseFlags "g") assert $ test pattern "a" assert $ test pattern "a" + +nea :: Array ~> NonEmptyArray +nea = unsafePartial fromJust <<< fromArray