From ecc6ad64321a59a9742bfce03a3e4ba165f5e3d5 Mon Sep 17 00:00:00 2001 From: Liam Goodacre Date: Thu, 16 Jul 2015 15:50:27 +0100 Subject: [PATCH 1/2] Uppercase/lowercase on Char Added * `Data.Char.toUpperChar :: Char -> Char` * `Data.Char.toLowerChar :: Char -> Char` Removed * `Data.Char.showChar :: Show Char` - collided with `showChar` defined in the prelude? --- docs/Data/Char.md | 16 ++++++++++++++++ docs/Data/String.md | 5 +++-- src/Data/Char.js | 8 ++++++++ src/Data/Char.purs | 12 ++++++++---- 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/docs/Data/Char.md b/docs/Data/Char.md index c143a21..b8a61f9 100644 --- a/docs/Data/Char.md +++ b/docs/Data/Char.md @@ -26,4 +26,20 @@ fromCharCode :: Int -> Char Constructs a character from the given Unicode numeric value. +#### `toLowerChar` + +``` purescript +toLowerChar :: Char -> Char +``` + +Converts a character to lowercase. + +#### `toUpperChar` + +``` purescript +toUpperChar :: Char -> Char +``` + +Converts a character to uppercase. + diff --git a/docs/Data/String.md b/docs/Data/String.md index b5f4771..d9ad647 100644 --- a/docs/Data/String.md +++ b/docs/Data/String.md @@ -198,8 +198,9 @@ of the string for which the predicate holds. split :: String -> String -> Array String ``` -Returns the substrings of the first string separated along occurences -of the second string. +Returns the substrings of the second string separated along occurences +of the first string. +* `split " " "hello world" == ["hello", "world"]` #### `toCharArray` diff --git a/src/Data/Char.js b/src/Data/Char.js index b4885a6..374b885 100644 --- a/src/Data/Char.js +++ b/src/Data/Char.js @@ -14,3 +14,11 @@ exports.toCharCode = function (c) { exports.fromCharCode = function (c) { return String.fromCharCode(c); }; + +exports.toLowerChar = function (c) { + return c.toLowerCase(); +}; + +exports.toUpperChar = function (c) { + return c.toUpperCase(); +}; diff --git a/src/Data/Char.purs b/src/Data/Char.purs index 207890d..0548139 100644 --- a/src/Data/Char.purs +++ b/src/Data/Char.purs @@ -3,6 +3,8 @@ module Data.Char ( toString , fromCharCode , toCharCode + , toLowerChar + , toUpperChar ) where import Prelude @@ -16,11 +18,13 @@ foreign import toCharCode :: Char -> Int -- | Constructs a character from the given Unicode numeric value. foreign import fromCharCode :: Int -> Char +-- | Converts a character to lowercase. +foreign import toLowerChar :: Char -> Char + +-- | Converts a character to uppercase. +foreign import toUpperChar :: Char -> Char + -- | Characters fall within the Unicode range. instance boundedChar :: Bounded Char where top = fromCharCode zero bottom = fromCharCode 65535 - --- | Characters can be rendered as a string with `show`. -instance showChar :: Show Char where - show c = "Char " ++ show (toString c) From 1523fc3c2b6dc8d2f05dcc00cb5deb85e0c47514 Mon Sep 17 00:00:00 2001 From: Liam Goodacre Date: Sat, 18 Jul 2015 00:24:13 +0100 Subject: [PATCH 2/2] tests, changes, and fixes Testing -- * Added `test/` directory. * Added `pulp test` to the `build` script. * New dev dependencies `purescript-assert` and `purescript-console` Breaking changes -- * `Data.String.Regex.search` now gives back `Maybe Int`. * `Nothing` for no match instead of `-1`. * `Data.Char.toUpperChar` is now `Data.Char.toUpper`. * `Data.Char.toLowerChar` is now `Data.Char.toLower`. Bug fixes -- * `Data.String.indexOf'` wrong for index out of bounds. * `Data.String.lastIndexOf'` wrong for index out of bounds. * `Data.String.localeCompare` off by 1. Notes -- The vast majority of functions have at least one test case. Lots of room for more though, especially in `Data.String.Regex`! Tests could also be added for checking that the unsafe functions throw in the documented ways. --- bower.json | 4 + docs/Data/Char.md | 8 +- docs/Data/String/Regex.md | 6 +- package.json | 2 +- src/Data/Char.js | 4 +- src/Data/Char.purs | 8 +- src/Data/String.js | 4 +- src/Data/String.purs | 4 +- src/Data/String/Regex.js | 11 +- src/Data/String/Regex.purs | 13 ++- test/Test/Data/Char.purs | 27 +++++ test/Test/Data/String.purs | 175 ++++++++++++++++++++++++++++++ test/Test/Data/String/Regex.purs | 31 ++++++ test/Test/Data/String/Unsafe.purs | 18 +++ test/Test/Main.purs | 13 +++ 15 files changed, 305 insertions(+), 23 deletions(-) create mode 100644 test/Test/Data/Char.purs create mode 100644 test/Test/Data/String.purs create mode 100644 test/Test/Data/String/Regex.purs create mode 100644 test/Test/Data/String/Unsafe.purs create mode 100644 test/Test/Main.purs diff --git a/bower.json b/bower.json index b4c32dc..f9aed89 100644 --- a/bower.json +++ b/bower.json @@ -20,5 +20,9 @@ ], "dependencies": { "purescript-maybe": "^0.3.0" + }, + "devDependencies": { + "purescript-assert": "~0.1.0", + "purescript-console": "~0.1.0" } } diff --git a/docs/Data/Char.md b/docs/Data/Char.md index b8a61f9..8a63047 100644 --- a/docs/Data/Char.md +++ b/docs/Data/Char.md @@ -26,18 +26,18 @@ fromCharCode :: Int -> Char Constructs a character from the given Unicode numeric value. -#### `toLowerChar` +#### `toLower` ``` purescript -toLowerChar :: Char -> Char +toLower :: Char -> Char ``` Converts a character to lowercase. -#### `toUpperChar` +#### `toUpper` ``` purescript -toUpperChar :: Char -> Char +toUpper :: Char -> Char ``` Converts a character to uppercase. diff --git a/docs/Data/String/Regex.md b/docs/Data/String/Regex.md index 95a6150..bcc0427 100644 --- a/docs/Data/String/Regex.md +++ b/docs/Data/String/Regex.md @@ -115,11 +115,11 @@ See the [reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe #### `search` ``` purescript -search :: Regex -> String -> Int +search :: Regex -> String -> Maybe Int ``` -Returns the index of the first match of the `Regex` in the string, or -`-1` if there is no match. +Returns `Just` the index of the first match of the `Regex` in the string, +or `Nothing` if there is no match. #### `split` diff --git a/package.json b/package.json index e129c49..2d38aab 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "private": true, "scripts": { "postinstall": "pulp dep install", - "build": "jshint src && jscs src && pulp build && rimraf docs && pulp docs" + "build": "jshint src && jscs src && pulp build && pulp test && rimraf docs && pulp docs" }, "devDependencies": { "jscs": "^1.13.1", diff --git a/src/Data/Char.js b/src/Data/Char.js index 374b885..7a929c6 100644 --- a/src/Data/Char.js +++ b/src/Data/Char.js @@ -15,10 +15,10 @@ exports.fromCharCode = function (c) { return String.fromCharCode(c); }; -exports.toLowerChar = function (c) { +exports.toLower = function (c) { return c.toLowerCase(); }; -exports.toUpperChar = function (c) { +exports.toUpper = function (c) { return c.toUpperCase(); }; diff --git a/src/Data/Char.purs b/src/Data/Char.purs index 0548139..17dd992 100644 --- a/src/Data/Char.purs +++ b/src/Data/Char.purs @@ -3,8 +3,8 @@ module Data.Char ( toString , fromCharCode , toCharCode - , toLowerChar - , toUpperChar + , toLower + , toUpper ) where import Prelude @@ -19,10 +19,10 @@ foreign import toCharCode :: Char -> Int foreign import fromCharCode :: Int -> Char -- | Converts a character to lowercase. -foreign import toLowerChar :: Char -> Char +foreign import toLower :: Char -> Char -- | Converts a character to uppercase. -foreign import toUpperChar :: Char -> Char +foreign import toUpper :: Char -> Char -- | Characters fall within the Unicode range. instance boundedChar :: Bounded Char where diff --git a/src/Data/String.js b/src/Data/String.js index 5e47ced..bf50543 100644 --- a/src/Data/String.js +++ b/src/Data/String.js @@ -51,6 +51,7 @@ exports["_indexOf'"] = function (just) { return function (x) { return function (startAt) { return function (s) { + if (startAt < 0 || startAt > s.length) return nothing; var i = s.indexOf(x, startAt); return i === -1 ? nothing : just(i); }; @@ -75,6 +76,7 @@ exports["_lastIndexOf'"] = function (just) { return function (x) { return function (startAt) { return function (s) { + if (startAt < 0 || startAt > s.length) return nothing; var i = s.lastIndexOf(x, startAt); return i === -1 ? nothing : just(i); }; @@ -93,7 +95,7 @@ exports._localeCompare = function (lt) { return function (s1) { return function (s2) { var result = s1.localeCompare(s2); - return result < 0 ? lt : result > 1 ? gt : eq; + return result < 0 ? lt : result > 0 ? gt : eq; }; }; }; diff --git a/src/Data/String.purs b/src/Data/String.purs index 652d077..7af4fed 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -33,7 +33,7 @@ module Data.String ) where import Prelude -import Data.Char +import qualified Data.Char as C import Data.Maybe (Maybe(..), isJust) import Data.Monoid (Monoid) import qualified Data.String.Unsafe as U @@ -50,7 +50,7 @@ foreign import _charAt :: (forall a. a -> Maybe a) -- | Returns a string of length `1` containing the given character. fromChar :: Char -> String -fromChar = toString +fromChar = C.toString -- | Returns a string of length `1` containing the given character. -- | Same as `fromChar`. diff --git a/src/Data/String/Regex.js b/src/Data/String/Regex.js index 01e8a2e..f70bef3 100644 --- a/src/Data/String/Regex.js +++ b/src/Data/String/Regex.js @@ -70,9 +70,14 @@ exports["replace'"] = function (r) { }; }; -exports.search = function (r) { - return function (s) { - return s.search(r); +exports._search = function (just) { + return function (nothing) { + return function (r) { + return function (s) { + var result = s.search(r); + return result === -1 ? nothing : just(result); + }; + }; }; }; diff --git a/src/Data/String/Regex.purs b/src/Data/String/Regex.purs index 3bec5b9..d0bf1ea 100644 --- a/src/Data/String/Regex.purs +++ b/src/Data/String/Regex.purs @@ -104,9 +104,16 @@ foreign import replace :: Regex -> String -> String -> String -- | See the [reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_function_as_a_parameter). foreign import replace' :: Regex -> (String -> Array String -> String) -> String -> String --- | Returns the index of the first match of the `Regex` in the string, or --- | `-1` if there is no match. -foreign import search :: Regex -> String -> Int +foreign import _search :: (forall r. r -> Maybe r) + -> (forall r. Maybe r) + -> Regex + -> String + -> Maybe Int + +-- | Returns `Just` the index of the first match of the `Regex` in the string, +-- | or `Nothing` if there is no match. +search :: Regex -> String -> Maybe Int +search = _search Just Nothing -- | Split the string into an array of substrings along occurences of the `Regex`. foreign import split :: Regex -> String -> Array String diff --git a/test/Test/Data/Char.purs b/test/Test/Data/Char.purs new file mode 100644 index 0000000..5063b74 --- /dev/null +++ b/test/Test/Data/Char.purs @@ -0,0 +1,27 @@ +module Test.Data.Char (testChar) where + +import Prelude +import Control.Monad.Eff.Console (log) +import Data.Char +import Test.Assert (assert) + +testChar = do + log "toString" + assert $ toString 'a' == "a" + + log "toCharCode" + assert $ toCharCode 'a' == 97 + assert $ toCharCode '\n' == 10 + + log "fromCharCode" + assert $ fromCharCode 97 == 'a' + assert $ fromCharCode 10 == '\n' + + log "toLower" + assert $ toLower 'A' == 'a' + assert $ toLower 'a' == 'a' + + log "toUpper" + assert $ toUpper 'a' == 'A' + assert $ toUpper 'A' == 'A' + diff --git a/test/Test/Data/String.purs b/test/Test/Data/String.purs new file mode 100644 index 0000000..4f43ac3 --- /dev/null +++ b/test/Test/Data/String.purs @@ -0,0 +1,175 @@ +module Test.Data.String (testString) where + +import Prelude +import Data.Maybe +import Control.Monad.Eff.Console (log) +import Data.String +import Test.Assert (assert) + +testString = do + log "charAt" + assert $ charAt 0 "" == Nothing + assert $ charAt 0 "a" == Just 'a' + assert $ charAt 1 "a" == Nothing + assert $ charAt 0 "ab" == Just 'a' + assert $ charAt 1 "ab" == Just 'b' + assert $ charAt 2 "ab" == Nothing + + log "fromChar" + assert $ fromChar 'a' == "a" + + log "singleton" + assert $ singleton 'a' == "a" + + log "charCodeAt" + assert $ charCodeAt 0 "" == Nothing + assert $ charCodeAt 0 "a" == Just 97 + assert $ charCodeAt 1 "a" == Nothing + assert $ charCodeAt 0 "ab" == Just 97 + assert $ charCodeAt 1 "ab" == Just 98 + assert $ charCodeAt 2 "ab" == Nothing + + log "toChar" + assert $ toChar "" == Nothing + assert $ toChar "a" == Just 'a' + assert $ toChar "ab" == Nothing + + log "null" + assert $ null "" + assert $ not (null "a") + + log "uncons" + assert $ isNothing (uncons "") + assert $ case uncons "a" of + Nothing -> false + Just m -> m.head == 'a' && m.tail == "" + assert $ case uncons "ab" of + Nothing -> false + Just m -> m.head == 'a' && m.tail == "b" + + log "takeWhile" + assert $ takeWhile (\c -> true) "abc" == "abc" + assert $ takeWhile (\c -> false) "abc" == "" + assert $ takeWhile (\c -> c /= 'b') "aabbcc" == "aa" + + log "dropWhile" + assert $ dropWhile (\c -> true) "abc" == "" + assert $ dropWhile (\c -> false) "abc" == "abc" + assert $ dropWhile (\c -> c /= 'b') "aabbcc" == "bbcc" + + log "stripPrefix" + assert $ stripPrefix "" "" == Just "" + assert $ stripPrefix "" "abc" == Just "abc" + assert $ stripPrefix "a" "abc" == Just "bc" + assert $ stripPrefix "!" "abc" == Nothing + assert $ stripPrefix "!" "" == Nothing + + log "fromCharArray" + assert $ fromCharArray [] == "" + assert $ fromCharArray ['a', 'b'] == "ab" + + log "contains" + assert $ contains "" "" + assert $ contains "" "abcd" + assert $ contains "bc" "abcd" + assert $ not (contains "cb" "abcd") + + log "indexOf" + assert $ indexOf "" "" == Just 0 + assert $ indexOf "" "abcd" == Just 0 + assert $ indexOf "bc" "abcd" == Just 1 + assert $ indexOf "cb" "abcd" == Nothing + + log "indexOf'" + assert $ indexOf' "" 0 "" == Just 0 + assert $ indexOf' "" (-1) "ab" == Nothing + assert $ indexOf' "" 0 "ab" == Just 0 + assert $ indexOf' "" 1 "ab" == Just 1 + assert $ indexOf' "" 2 "ab" == Just 2 + assert $ indexOf' "" 3 "ab" == Nothing + assert $ indexOf' "bc" 0 "abcd" == Just 1 + assert $ indexOf' "bc" 1 "abcd" == Just 1 + assert $ indexOf' "bc" 2 "abcd" == Nothing + assert $ indexOf' "cb" 0 "abcd" == Nothing + + log "lastIndexOf" + assert $ lastIndexOf "" "" == Just 0 + assert $ lastIndexOf "" "abcd" == Just 4 + assert $ lastIndexOf "bc" "abcd" == Just 1 + assert $ lastIndexOf "cb" "abcd" == Nothing + + log "lastIndexOf'" + assert $ lastIndexOf' "" 0 "" == Just 0 + assert $ lastIndexOf' "" (-1) "ab" == Nothing + assert $ lastIndexOf' "" 0 "ab" == Just 0 + assert $ lastIndexOf' "" 1 "ab" == Just 1 + assert $ lastIndexOf' "" 2 "ab" == Just 2 + assert $ lastIndexOf' "" 3 "ab" == Nothing + assert $ lastIndexOf' "bc" 0 "abcd" == Nothing + assert $ lastIndexOf' "bc" 1 "abcd" == Just 1 + assert $ lastIndexOf' "bc" 2 "abcd" == Just 1 + assert $ lastIndexOf' "cb" 0 "abcd" == Nothing + + log "length" + assert $ length "" == 0 + assert $ length "a" == 1 + assert $ length "ab" == 2 + + log "localeCompare" + assert $ localeCompare "" "" == EQ + assert $ localeCompare "a" "a" == EQ + assert $ localeCompare "a" "b" == LT + assert $ localeCompare "b" "a" == GT + + log "replace" + assert $ replace "b" "" "abc" == "ac" + assert $ replace "b" "!" "abc" == "a!c" + assert $ replace "d" "!" "abc" == "abc" + + log "take" + assert $ take 0 "ab" == "" + assert $ take 1 "ab" == "a" + assert $ take 2 "ab" == "ab" + assert $ take 3 "ab" == "ab" + + log "drop" + assert $ drop 0 "ab" == "ab" + assert $ drop 1 "ab" == "b" + assert $ drop 2 "ab" == "" + assert $ drop 3 "ab" == "" + + log "count" + assert $ count (\c -> true) "" == 0 + assert $ count (\c -> true) "ab" == 2 + assert $ count (\c -> false) "ab" == 0 + assert $ count (\c -> c == 'a') "aabbcc" == 2 + assert $ count (\c -> c == 'b') "aabbcc" == 0 + assert $ count (\c -> c /= 'a') "aabbcc" == 0 + assert $ count (\c -> c /= 'b') "aabbcc" == 2 + + log "split" + assert $ split "" "" == [] + assert $ split "" "a" == ["a"] + assert $ split "" "ab" == ["a", "b"] + assert $ split "b" "aabcc" == ["aa", "cc"] + assert $ split "d" "abc" == ["abc"] + + log "toCharArray" + assert $ toCharArray "" == [] + assert $ toCharArray "a" == ['a'] + assert $ toCharArray "ab" == ['a', 'b'] + + log "toLower" + assert $ toLower "bAtMaN" == "batman" + + log "toUpper" + assert $ toUpper "bAtMaN" == "BATMAN" + + log "trim" + assert $ trim " abc " == "abc" + + log "joinWith" + assert $ joinWith "" [] == "" + assert $ joinWith "" ["a", "b"] == "ab" + assert $ joinWith "--" ["a", "b", "c"] == "a--b--c" + diff --git a/test/Test/Data/String/Regex.purs b/test/Test/Data/String/Regex.purs new file mode 100644 index 0000000..bcf708a --- /dev/null +++ b/test/Test/Data/String/Regex.purs @@ -0,0 +1,31 @@ +module Test.Data.String.Regex (testStringRegex) where + +import Prelude +import Data.Maybe +import Control.Monad.Eff.Console (log) +import Data.String.Regex +import Test.Assert (assert) + +testStringRegex = do + log "regex" + assert $ test (regex "^a" noFlags) "abc" + assert $ not (test (regex "^b" noFlags) "abc") + + log "match" + assert $ match (regex "^abc$" noFlags) "abc" == Just [Just "abc"] + + log "replace" + assert $ replace (regex "-" noFlags) "!" "a-b-c" == "a!b-c" + + log "replace'" + assert $ replace' (regex "-" noFlags) (\s xs -> "!") "a-b-c" == "a!b-c" + + log "search" + assert $ search (regex "b" noFlags) "abc" == Just 1 + assert $ search (regex "d" noFlags) "abc" == Nothing + + log "split" + assert $ split (regex "" noFlags) "" == [] + assert $ split (regex "" noFlags) "abc" == ["a", "b", "c"] + assert $ split (regex "b" noFlags) "" == [""] + assert $ split (regex "b" noFlags) "abc" == ["a", "c"] diff --git a/test/Test/Data/String/Unsafe.purs b/test/Test/Data/String/Unsafe.purs new file mode 100644 index 0000000..de9a53c --- /dev/null +++ b/test/Test/Data/String/Unsafe.purs @@ -0,0 +1,18 @@ +module Test.Data.String.Unsafe (testStringUnsafe) where + +import Prelude +import Control.Monad.Eff.Console (log) +import Data.String.Unsafe +import Test.Assert (assert) + +testStringUnsafe = do + log "charCodeAt" + assert $ charCodeAt 0 "ab" == 97 + assert $ charCodeAt 1 "ab" == 98 + + log "charAt" + assert $ charAt 0 "ab" == 'a' + assert $ charAt 1 "ab" == 'b' + + log "char" + assert $ char "a" == 'a' diff --git a/test/Test/Main.purs b/test/Test/Main.purs new file mode 100644 index 0000000..78bbcf2 --- /dev/null +++ b/test/Test/Main.purs @@ -0,0 +1,13 @@ +module Test.Main where + +import Prelude +import Test.Data.Char +import Test.Data.String +import Test.Data.String.Unsafe +import Test.Data.String.Regex + +main = do + testChar + testString + testStringUnsafe + testStringRegex