Skip to content

Commit 62c9fdc

Browse files
authored
Merge pull request #87 from MonoidMusician/total-splitat
Make splitAt total
2 parents 00030c2 + 90996da commit 62c9fdc

File tree

10 files changed

+94
-93
lines changed

10 files changed

+94
-93
lines changed

src/Data/String.js

+3-9
Original file line numberDiff line numberDiff line change
@@ -153,15 +153,9 @@ exports.split = function (sep) {
153153
};
154154
};
155155

156-
exports._splitAt = function (just) {
157-
return function (nothing) {
158-
return function (i) {
159-
return function (s) {
160-
return i >= 0 && i < s.length ?
161-
just({ before: s.substring(0, i), after: s.substring(i) }) :
162-
nothing;
163-
};
164-
};
156+
exports.splitAt = function (i) {
157+
return function (s) {
158+
return { before: s.substring(0, i), after: s.substring(i) };
165159
};
166160
};
167161

src/Data/String.purs

+16-11
Original file line numberDiff line numberDiff line change
@@ -421,21 +421,26 @@ foreign import count :: (Char -> Boolean) -> String -> Int
421421
-- |
422422
foreign import split :: Pattern -> String -> Array String
423423

424-
-- | Returns the substrings of a split at the given index, if the index is within bounds.
424+
-- | Splits a string into two substrings, where `before` contains the
425+
-- | characters up to (but not including) the given index, and `after` contains
426+
-- | the rest of the string, from that index on.
425427
-- |
426428
-- | ```purescript
427-
-- | splitAt 2 "Hello World" == Just { before: "He", after: "llo World"}
428-
-- | splitAt 10 "Hi" == Nothing
429+
-- | splitAt 2 "Hello World" == { before: "He", after: "llo World"}
430+
-- | splitAt 10 "Hi" == { before: "Hi", after: ""}
429431
-- | ```
430432
-- |
431-
splitAt :: Int -> String -> Maybe { before :: String, after :: String }
432-
splitAt = _splitAt Just Nothing
433-
434-
foreign import _splitAt :: (forall a. a -> Maybe a)
435-
-> (forall a. Maybe a)
436-
-> Int
437-
-> String
438-
-> Maybe { before :: String, after :: String }
433+
-- | Thus the length of `(splitAt i s).before` will equal either `i` or
434+
-- | `length s`, if that is shorter. (Or if `i` is negative the length will be
435+
-- | 0.)
436+
-- |
437+
-- | In code:
438+
-- | ```purescript
439+
-- | length (splitAt i s).before == min (max i 0) (length s)
440+
-- | (splitAt i s).before <> (splitAt i s).after == s
441+
-- | splitAt i s == {before: take i s, after: drop i s}
442+
-- | ```
443+
foreign import splitAt :: Int -> String -> { before :: String, after :: String }
439444

440445
-- | Converts the string into an array of characters.
441446
-- |

src/Data/String/CodePoints.purs

+19-12
Original file line numberDiff line numberDiff line change
@@ -340,25 +340,32 @@ singletonFallback (CodePoint cp) =
340340
fromCharCode lead <> fromCharCode trail
341341

342342

343-
-- | Returns a record with strings created from the code points on either side
344-
-- | of the given index. If the index is not within the string, Nothing is
345-
-- | returned.
343+
-- | Splits a string into two substrings, where `before` contains the code
344+
-- | points up to (but not including) the given index, and `after` contains the
345+
-- | rest of the string, from that index on.
346346
-- |
347347
-- | ```purescript
348348
-- | >>> splitAt 3 "b 𝐀𝐀 c 𝐀"
349349
-- | Just { before: "b 𝐀", after: "𝐀 c 𝐀" }
350350
-- | ```
351351
-- |
352-
splitAt :: Int -> String -> Maybe { before :: String, after :: String }
352+
-- | Thus the length of `(splitAt i s).before` will equal either `i` or
353+
-- | `length s`, if that is shorter. (Or if `i` is negative the length will be
354+
-- | 0.)
355+
-- |
356+
-- | In code:
357+
-- | ```purescript
358+
-- | length (splitAt i s).before == min (max i 0) (length s)
359+
-- | (splitAt i s).before <> (splitAt i s).after == s
360+
-- | splitAt i s == {before: take i s, after: drop i s}
361+
-- | ```
362+
splitAt :: Int -> String -> { before :: String, after :: String }
353363
splitAt i s =
354-
let cps = toCodePointArray s in
355-
if i < 0 || Array.length cps < i
356-
then Nothing
357-
else Just {
358-
before: fromCodePointArray (Array.take i cps),
359-
after: fromCodePointArray (Array.drop i cps)
360-
}
361-
364+
let before = take i s in
365+
{ before
366+
-- inline drop i s to reuse the result of take i s
367+
, after: String.drop (String.length before) s
368+
}
362369

363370
-- | Returns a string containing the given number of code points from the
364371
-- | beginning of the given string. If the string does not have that many code

src/Data/String/NonEmpty.purs

+4-6
Original file line numberDiff line numberDiff line change
@@ -412,12 +412,10 @@ count = liftS <<< String.count
412412
splitAt
413413
:: Int
414414
-> NonEmptyString
415-
-> Maybe { before :: Maybe NonEmptyString, after :: Maybe NonEmptyString }
416-
splitAt i (NonEmptyString s) = case String.splitAt i s of
417-
Just { before, after } ->
418-
Just { before: fromString before, after: fromString after }
419-
Nothing ->
420-
Nothing
415+
-> { before :: Maybe NonEmptyString, after :: Maybe NonEmptyString }
416+
splitAt i (NonEmptyString s) =
417+
case String.splitAt i s of
418+
{ before, after } -> { before: fromString before, after: fromString after }
421419

422420
-- | Returns the argument converted to lowercase.
423421
-- |

src/Data/String/Regex.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ exports._match = function (just) {
4646
return function (r) {
4747
return function (s) {
4848
var m = s.match(r);
49-
if (m == null) {
49+
if (m == null || m.length === 0) {
5050
return nothing;
5151
} else {
5252
for (var i = 0; i < m.length; i++) {

src/Data/String/Regex.purs

+3-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ module Data.String.Regex
1818

1919
import Prelude
2020

21+
import Data.Array.NonEmpty (NonEmptyArray)
2122
import Data.Either (Either(..))
2223
import Data.Maybe (Maybe(..))
2324
import Data.String (Pattern(..), contains)
@@ -82,13 +83,13 @@ foreign import _match
8283
-> (forall r. Maybe r)
8384
-> Regex
8485
-> String
85-
-> Maybe (Array (Maybe String))
86+
-> Maybe (NonEmptyArray (Maybe String))
8687

8788
-- | Matches the string against the `Regex` and returns an array of matches
8889
-- | if there were any. Each match has type `Maybe String`, where `Nothing`
8990
-- | represents an unmatched optional capturing group.
9091
-- | See [reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match).
91-
match :: Regex -> String -> Maybe (Array (Maybe String))
92+
match :: Regex -> String -> Maybe (NonEmptyArray (Maybe String))
9293
match = _match Just Nothing
9394

9495
-- | Replaces occurences of the `Regex` with the first string. The replacement

test/Test/Data/String.purs

+12-13
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import Prelude (Unit, Ordering(..), (==), ($), discard, negate, not, (/=), (&&))
55
import Effect (Effect)
66
import Effect.Console (log)
77

8-
import Data.Maybe (Maybe(..), isNothing, maybe)
8+
import Data.Maybe (Maybe(..), isNothing)
99
import Data.String
1010

1111
import Test.Assert (assert)
@@ -174,19 +174,18 @@ testString = do
174174
assert $ split (Pattern "d") "abc" == ["abc"]
175175

176176
log "splitAt"
177-
let testSplitAt i str res =
177+
let testSplitAt i str r =
178178
assert $ case splitAt i str of
179-
Nothing ->
180-
isNothing res
181-
Just { before, after } ->
182-
maybe false (\r ->
183-
r.before == before && r.after == after) res
184-
185-
testSplitAt 1 "" Nothing
186-
testSplitAt 0 "a" $ Just {before: "", after: "a"}
187-
testSplitAt 1 "ab" $ Just {before: "a", after: "b"}
188-
testSplitAt 3 "aabcc" $ Just {before: "aab", after: "cc"}
189-
testSplitAt (-1) "abc" $ Nothing
179+
{ before, after } ->
180+
r.before == before && r.after == after
181+
182+
testSplitAt 1 "" {before: "", after: ""}
183+
testSplitAt 0 "a" {before: "", after: "a"}
184+
testSplitAt 1 "a" {before: "a", after: ""}
185+
testSplitAt 1 "ab" {before: "a", after: "b"}
186+
testSplitAt 3 "aabcc" {before: "aab", after: "cc"}
187+
testSplitAt (-1) "abc" {before: "", after: "abc"}
188+
testSplitAt 10 "Hi" {before: "Hi", after: ""}
190189

191190
log "toCharArray"
192191
assert $ toCharArray "" == []

test/Test/Data/String/CodePoints.purs

+19-22
Original file line numberDiff line numberDiff line change
@@ -152,29 +152,26 @@ testStringCodePoints = do
152152
assert $ (singleton <$> codePointFromInt 0x16805) == Just "\x16805"
153153

154154
log "splitAt"
155-
let testSplitAt i s res =
155+
let testSplitAt i s r =
156156
assert $ case splitAt i s of
157-
Nothing ->
158-
isNothing res
159-
Just { before, after } ->
160-
maybe false (\r ->
161-
r.before == before && r.after == after) res
162-
163-
testSplitAt 0 "" $ Just {before: "", after: ""}
164-
testSplitAt 1 "" Nothing
165-
testSplitAt 0 "a" $ Just {before: "", after: "a"}
166-
testSplitAt 1 "ab" $ Just {before: "a", after: "b"}
167-
testSplitAt 3 "aabcc" $ Just {before: "aab", after: "cc"}
168-
testSplitAt (-1) "abc" $ Nothing
169-
testSplitAt 0 str $ Just {before: "", after: str}
170-
testSplitAt 1 str $ Just {before: "a", after: "\xDC00\xD800\xD800\x16805\x16A06\&z"}
171-
testSplitAt 2 str $ Just {before: "a\xDC00", after: "\xD800\xD800\x16805\x16A06\&z"}
172-
testSplitAt 3 str $ Just {before: "a\xDC00\xD800", after: "\xD800\x16805\x16A06\&z"}
173-
testSplitAt 4 str $ Just {before: "a\xDC00\xD800\xD800", after: "\x16805\x16A06\&z"}
174-
testSplitAt 5 str $ Just {before: "a\xDC00\xD800\xD800\x16805", after: "\x16A06\&z"}
175-
testSplitAt 6 str $ Just {before: "a\xDC00\xD800\xD800\x16805\x16A06", after: "z"}
176-
testSplitAt 7 str $ Just {before: str, after: ""}
177-
testSplitAt 8 str $ Nothing
157+
{ before, after } ->
158+
r.before == before && r.after == after
159+
160+
testSplitAt 0 "" {before: "", after: "" }
161+
testSplitAt 1 "" {before: "", after: "" }
162+
testSplitAt 0 "a" {before: "", after: "a"}
163+
testSplitAt 1 "ab" {before: "a", after: "b"}
164+
testSplitAt 3 "aabcc" {before: "aab", after: "cc"}
165+
testSplitAt (-1) "abc" {before: "", after: "abc"}
166+
testSplitAt 0 str {before: "", after: str}
167+
testSplitAt 1 str {before: "a", after: "\xDC00\xD800\xD800\x16805\x16A06\&z"}
168+
testSplitAt 2 str {before: "a\xDC00", after: "\xD800\xD800\x16805\x16A06\&z"}
169+
testSplitAt 3 str {before: "a\xDC00\xD800", after: "\xD800\x16805\x16A06\&z"}
170+
testSplitAt 4 str {before: "a\xDC00\xD800\xD800", after: "\x16805\x16A06\&z"}
171+
testSplitAt 5 str {before: "a\xDC00\xD800\xD800\x16805", after: "\x16A06\&z"}
172+
testSplitAt 6 str {before: "a\xDC00\xD800\xD800\x16805\x16A06", after: "z"}
173+
testSplitAt 7 str {before: str, after: ""}
174+
testSplitAt 8 str {before: str, after: ""}
178175

179176
log "take"
180177
assert $ take (-1) str == ""

test/Test/Data/String/NonEmpty.purs

+5-9
Original file line numberDiff line numberDiff line change
@@ -203,15 +203,11 @@ testNonEmptyString = do
203203
let
204204
testSplitAt i str res =
205205
assert $ case splitAt i str of
206-
Nothing ->
207-
isNothing res
208-
Just { before, after } ->
209-
maybe false (\r ->
210-
r.before == before && r.after == after) res
211-
testSplitAt 0 (nes "a") (Just { before: Nothing, after: Just (nes "a") })
212-
testSplitAt 1 (nes "ab") (Just { before: Just (nes "a"), after: Just (nes "b") })
213-
testSplitAt 3 (nes "aabcc") (Just { before: Just (nes "aab"), after: Just (nes "cc") })
214-
testSplitAt (-1) (nes "abc") Nothing
206+
{ before, after } -> res.before == before && res.after == after
207+
testSplitAt 0 (nes "a") { before: Nothing, after: Just (nes "a") }
208+
testSplitAt 1 (nes "ab") { before: Just (nes "a"), after: Just (nes "b") }
209+
testSplitAt 3 (nes "aabcc") { before: Just (nes "aab"), after: Just (nes "cc") }
210+
testSplitAt (-1) (nes "abc") { before: Nothing, after: Just (nes "abc") }
215211

216212
log "toLower"
217213
assert $ toLower (nes "bAtMaN") == nes "batman"

test/Test/Data/String/Regex.purs

+12-8
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
module Test.Data.String.Regex (testStringRegex) where
22

3-
import Prelude (Unit, ($), (<>), discard, (==), not)
4-
5-
import Effect (Effect)
6-
import Effect.Console (log)
3+
import Data.String.Regex
74

5+
import Data.Array.NonEmpty (NonEmptyArray, fromArray)
86
import Data.Either (isLeft)
9-
import Data.Maybe (Maybe(..))
10-
import Data.String.Regex
7+
import Data.Maybe (Maybe(..), fromJust)
118
import Data.String.Regex.Flags (global, ignoreCase, noFlags)
129
import Data.String.Regex.Unsafe (unsafeRegex)
13-
10+
import Effect (Effect)
11+
import Effect.Console (log)
12+
import Partial.Unsafe (unsafePartial)
13+
import Prelude (type (~>), Unit, discard, not, ($), (<<<), (<>), (==))
1414
import Test.Assert (assert)
1515

1616
testStringRegex :: Effect Unit
@@ -26,7 +26,8 @@ testStringRegex = do
2626
assert $ "quxbarquxbaz" == replace (unsafeRegex "foo" (global <> ignoreCase)) "qux" "foobarFOObaz"
2727

2828
log "match"
29-
assert $ match (unsafeRegex "^abc$" noFlags) "abc" == Just [Just "abc"]
29+
assert $ match (unsafeRegex "^abc$" noFlags) "abc" == Just (nea [Just "abc"])
30+
assert $ match (unsafeRegex "^abc$" noFlags) "xyz" == Nothing
3031

3132
log "replace"
3233
assert $ replace (unsafeRegex "-" noFlags) "!" "a-b-c" == "a!b-c"
@@ -50,3 +51,6 @@ testStringRegex = do
5051
let pattern = unsafeRegex "a" (parseFlags "g")
5152
assert $ test pattern "a"
5253
assert $ test pattern "a"
54+
55+
nea :: Array ~> NonEmptyArray
56+
nea = unsafePartial fromJust <<< fromArray

0 commit comments

Comments
 (0)