From ad0bf437dd8a47db12305e6329eb222e00faf96e Mon Sep 17 00:00:00 2001 From: Nicholas Scheel Date: Fri, 11 Aug 2017 16:30:52 -0500 Subject: [PATCH 1/6] Make splitAt total --- src/Data/String.js | 12 +++--------- src/Data/String.purs | 13 ++++--------- test/Test/Data/String.purs | 24 +++++++++++------------- 3 files changed, 18 insertions(+), 31 deletions(-) diff --git a/src/Data/String.js b/src/Data/String.js index c06bfcd..4286544 100644 --- a/src/Data/String.js +++ b/src/Data/String.js @@ -145,15 +145,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 6092ef6..7329d22 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -231,15 +231,10 @@ foreign import count :: (Char -> Boolean) -> String -> Int -- | * `split (Pattern " ") "hello world" == ["hello", "world"]` foreign import split :: Pattern -> String -> Array String --- | Returns the substrings of split at the given index, if the index is within bounds. -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 } +-- | Returns a string split into two substrings at the given index, where +-- | `before` includes all of the characters up to the given index, and `after` +-- | is the rest of the string, from the given index on. +foreign import splitAt :: Int -> String -> { before :: String, after :: String } -- | Converts the string into an array of characters. foreign import toCharArray :: String -> Array Char diff --git a/test/Test/Data/String.purs b/test/Test/Data/String.purs index 94cce8e..454d294 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 Control.Monad.Eff (Eff) import Control.Monad.Eff.Console (CONSOLE, log) -import Data.Maybe (Maybe(..), isNothing, maybe) +import Data.Maybe (Maybe(..), isNothing) import Data.String import Test.Assert (ASSERT, assert) @@ -160,19 +160,17 @@ 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" } log "toCharArray" assert $ toCharArray "" == [] From 9f5d65b32002262cb98a536552ae4f8b5ffaa12c Mon Sep 17 00:00:00 2001 From: Nicholas Scheel Date: Fri, 11 Aug 2017 17:07:31 -0500 Subject: [PATCH 2/6] Update documentation for splitAt --- src/Data/String.purs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Data/String.purs b/src/Data/String.purs index 7329d22..d6ef99e 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -232,8 +232,19 @@ foreign import count :: (Char -> Boolean) -> String -> Int foreign import split :: Pattern -> String -> Array String -- | Returns a string split into two substrings at the given index, where --- | `before` includes all of the characters up to the given index, and `after` --- | is the rest of the string, from the given index on. +-- | `before` includes all of the characters up to (but not including) the +-- | given index, and `after` is the rest of the string, from the given index +-- | on. +-- | +-- | 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 +-- | ``` foreign import splitAt :: Int -> String -> { before :: String, after :: String } -- | Converts the string into an array of characters. From d69ea5f1a9805bfaca16d9314e4db3fffd1292b7 Mon Sep 17 00:00:00 2001 From: Nicholas Scheel Date: Fri, 11 Aug 2017 17:42:48 -0500 Subject: [PATCH 3/6] Make change for CodePoints too --- src/Data/String/CodePoints.purs | 21 ++++++-------- test/Test/Data/String/CodePoints.purs | 41 +++++++++++++-------------- 2 files changed, 28 insertions(+), 34 deletions(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 7fe51f3..32ddcb2 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -214,19 +214,16 @@ 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. -splitAt :: Int -> String -> Maybe { before :: String, after :: String } +-- | 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. +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/test/Test/Data/String/CodePoints.purs b/test/Test/Data/String/CodePoints.purs index 7a6aef0..c5d305e 100644 --- a/test/Test/Data/String/CodePoints.purs +++ b/test/Test/Data/String/CodePoints.purs @@ -137,29 +137,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 == "" From 268ecdaf8a7fed21275c73ff7b4eb7796a166bd5 Mon Sep 17 00:00:00 2001 From: Nicholas Scheel Date: Fri, 11 Aug 2017 17:46:54 -0500 Subject: [PATCH 4/6] Consistency --- src/Data/String.purs | 8 ++++---- test/Test/Data/String.purs | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Data/String.purs b/src/Data/String.purs index d6ef99e..8aa18f5 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -231,10 +231,9 @@ foreign import count :: (Char -> Boolean) -> String -> Int -- | * `split (Pattern " ") "hello world" == ["hello", "world"]` foreign import split :: Pattern -> String -> Array String --- | Returns a string split into two substrings at the given index, where --- | `before` includes all of the characters up to (but not including) the --- | given index, and `after` is the rest of the string, from the given index --- | on. +-- | 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. -- | -- | 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 @@ -244,6 +243,7 @@ foreign import split :: Pattern -> String -> Array String -- | ```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 } diff --git a/test/Test/Data/String.purs b/test/Test/Data/String.purs index 454d294..8cd25f5 100644 --- a/test/Test/Data/String.purs +++ b/test/Test/Data/String.purs @@ -165,12 +165,12 @@ testString = do { 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 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"} log "toCharArray" assert $ toCharArray "" == [] From 51aac608607deb59dd95bd6f91cd41a3c06d30f2 Mon Sep 17 00:00:00 2001 From: Nicholas Scheel Date: Sun, 13 Aug 2017 12:14:37 -0500 Subject: [PATCH 5/6] Copy the rest of the comment over --- src/Data/String/CodePoints.purs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 32ddcb2..c0a45cd 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -217,6 +217,17 @@ singletonFallback (CodePoint cp) = -- | 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. +-- | +-- | 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 before = take i s in From d6c6b58e5b733899233f6dc9e8de1c83eafc719b Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Fri, 18 May 2018 15:39:11 +0100 Subject: [PATCH 6/6] Use NonEmptyArray for Regex match --- src/Data/String/Regex.js | 2 +- src/Data/String/Regex.purs | 5 +++-- test/Test/Data/String/Regex.purs | 20 ++++++++++++-------- 3 files changed, 16 insertions(+), 11 deletions(-) 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/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