From 134b6c32da6f8b5d4c0da14424b9a72e4e9db72d Mon Sep 17 00:00:00 2001 From: Isaac Shapira Date: Tue, 17 Feb 2015 23:30:15 -0700 Subject: [PATCH 001/186] Convenience data --- src/Data/String/Regex.purs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Data/String/Regex.purs b/src/Data/String/Regex.purs index 225f1b6..5f00939 100644 --- a/src/Data/String/Regex.purs +++ b/src/Data/String/Regex.purs @@ -38,6 +38,13 @@ type RegexFlags = , unicode :: Boolean } +noFlags :: RegexFlags +noFlags = { global : false + , ignoreCase : false + , multiline : false + , sticky : false + , unicode : false } + foreign import regex' """ function regex$prime(s1) { From 9a10b9451130518efe9473154760f48cdfe4c0ae Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Wed, 18 Feb 2015 19:57:14 +0000 Subject: [PATCH 002/186] Update to newer grunt plugin --- Gruntfile.js | 14 +++++++------- package.json | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 3c41546..e09e40c 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -2,18 +2,18 @@ module.exports = function(grunt) { "use strict"; - grunt.initConfig({ - + grunt.initConfig({ + libFiles: [ "src/**/*.purs", "bower_components/purescript-*/src/**/*.purs", ], - + clean: ["output"], - + pscMake: ["<%=libFiles%>"], dotPsci: ["<%=libFiles%>"], - docgen: { + pscDocs: { readme: { src: "src/**/*.purs", dest: "README.md" @@ -36,7 +36,7 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-jsvalidate'); grunt.loadNpmTasks("grunt-contrib-clean"); grunt.loadNpmTasks("grunt-purescript"); - - grunt.registerTask("make", ["pscMake", "dotPsci", "docgen", "jsvalidate"]); + + grunt.registerTask("make", ["pscMake", "dotPsci", "pscDocs", "jsvalidate"]); grunt.registerTask("default", ["make"]); }; diff --git a/package.json b/package.json index c2867b1..5f0dde2 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "private": true, "dependencies": { - "grunt": "~0.4.4", - "grunt-purescript": "~0.5.1", - "grunt-contrib-clean": "~0.5.0", - "grunt-jsvalidate": "~0.2.2" + "grunt": "^0.4.5", + "grunt-purescript": "^0.6.0", + "grunt-contrib-clean": "^0.5.0", + "grunt-jsvalidate": "^0.2.2" } } From a9a1fc66f18aff0fe17fb6b96fcb7a0840b3ea70 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Wed, 18 Feb 2015 19:57:20 +0000 Subject: [PATCH 003/186] Update docs --- README.md | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/README.md b/README.md index 770a294..69e656b 100644 --- a/README.md +++ b/README.md @@ -4,24 +4,38 @@ ### Types +#### `Char` + newtype Char ### Type Class Instances +#### `eqChar` + instance eqChar :: Eq Char +#### `ordChar` + instance ordChar :: Ord Char +#### `showChar` + instance showChar :: Show Char ### Values +#### `charString` + charString :: Char -> String +#### `fromCharCode` + fromCharCode :: Number -> Char +#### `toCharCode` + toCharCode :: Char -> Number @@ -29,54 +43,104 @@ ### Values +#### `charAt` + charAt :: Number -> String -> Maybe Char +#### `charCodeAt` + charCodeAt :: Number -> String -> Maybe Number +#### `count` + count :: (Char -> Boolean) -> String -> Number +#### `drop` + drop :: Number -> String -> String +#### `dropWhile` + dropWhile :: (Char -> Boolean) -> String -> String +#### `fromChar` + fromChar :: Char -> String +#### `fromCharArray` + fromCharArray :: [Char] -> String +#### `indexOf` + indexOf :: String -> String -> Number +#### `indexOf'` + indexOf' :: String -> Number -> String -> Number +#### `joinWith` + joinWith :: String -> [String] -> String +#### `lastIndexOf` + lastIndexOf :: String -> String -> Number +#### `lastIndexOf'` + lastIndexOf' :: String -> Number -> String -> Number +#### `length` + length :: String -> Number +#### `localeCompare` + localeCompare :: String -> String -> Number +#### `null` + null :: String -> Boolean +#### `replace` + replace :: String -> String -> String -> String +#### `singleton` + singleton :: Char -> String +#### `split` + split :: String -> String -> [String] +#### `take` + take :: Number -> String -> String +#### `takeWhile` + takeWhile :: (Char -> Boolean) -> String -> String +#### `toCharArray` + toCharArray :: String -> [Char] +#### `toLower` + toLower :: String -> String +#### `toUpper` + toUpper :: String -> String +#### `trim` + trim :: String -> String +#### `uncons` + uncons :: String -> Maybe { tail :: String, head :: Char } @@ -84,38 +148,66 @@ ### Types +#### `Regex` + data Regex :: * +#### `RegexFlags` + type RegexFlags = { unicode :: Boolean, sticky :: Boolean, multiline :: Boolean, ignoreCase :: Boolean, global :: Boolean } ### Type Class Instances +#### `showRegex` + instance showRegex :: Show Regex ### Values +#### `flags` + flags :: Regex -> RegexFlags +#### `match` + match :: Regex -> String -> Maybe [String] +#### `parseFlags` + parseFlags :: String -> RegexFlags +#### `regex` + regex :: String -> RegexFlags -> Regex +#### `renderFlags` + renderFlags :: RegexFlags -> String +#### `replace` + replace :: Regex -> String -> String -> String +#### `replace'` + replace' :: Regex -> (String -> [String] -> String) -> String -> String +#### `search` + search :: Regex -> String -> Number +#### `source` + source :: Regex -> String +#### `split` + split :: Regex -> String -> [String] +#### `test` + test :: Regex -> String -> Boolean @@ -123,6 +215,10 @@ ### Values +#### `charAt` + charAt :: Number -> String -> Char +#### `charCodeAt` + charCodeAt :: Number -> String -> Number \ No newline at end of file From 02376e803dcf2e48de3e92fe7a292429d22f760c Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Wed, 18 Feb 2015 20:00:14 +0000 Subject: [PATCH 004/186] Export noFlags --- README.md | 4 ++++ src/Data/String/Regex.purs | 29 +++++++++++++++-------------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 69e656b..410cbdd 100644 --- a/README.md +++ b/README.md @@ -174,6 +174,10 @@ match :: Regex -> String -> Maybe [String] +#### `noFlags` + + noFlags :: RegexFlags + #### `parseFlags` parseFlags :: String -> RegexFlags diff --git a/src/Data/String/Regex.purs b/src/Data/String/Regex.purs index 5f00939..6ad1bce 100644 --- a/src/Data/String/Regex.purs +++ b/src/Data/String/Regex.purs @@ -1,17 +1,18 @@ -module Data.String.Regex ( - Regex(..), - RegexFlags(..), - regex, - source, - flags, - renderFlags, - parseFlags, - test, - match, - replace, - replace', - search, - split +module Data.String.Regex + ( Regex(..) + , RegexFlags(..) + , regex + , source + , flags + , renderFlags + , parseFlags + , test + , match + , replace + , replace' + , search + , split + , noFlags ) where import Data.Function From 2f3726a5b040f334525c01e4d8cde74541420f63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Boros?= Date: Fri, 20 Mar 2015 17:34:39 +0100 Subject: [PATCH 005/186] add documentation --- README.md | 388 ++++++++++++++++++++++++++++-------- src/Data/Char/Char.purs | 5 + src/Data/String.purs | 48 ++++- src/Data/String/Regex.purs | 24 +++ src/Data/String/Unsafe.purs | 7 + 5 files changed, 385 insertions(+), 87 deletions(-) diff --git a/README.md b/README.md index 410cbdd..8052e4e 100644 --- a/README.md +++ b/README.md @@ -2,227 +2,445 @@ ## Module Data.Char -### Types + +A type and functions for single characters. #### `Char` - newtype Char +``` purescript +newtype Char +``` + +A unicode character. + +#### `charString` + +``` purescript +charString :: Char -> String +``` + +Returns the string of length `1` containing only the given character. + +#### `toCharCode` + +``` purescript +toCharCode :: Char -> Number +``` +Returns the numeric Unicode value of the character. + +#### `fromCharCode` -### Type Class Instances +``` purescript +fromCharCode :: Number -> Char +``` + +Constructs a character from the given Unicode numeric value. #### `eqChar` - instance eqChar :: Eq Char +``` purescript +instance eqChar :: Eq Char +``` + #### `ordChar` - instance ordChar :: Ord Char +``` purescript +instance ordChar :: Ord Char +``` + #### `showChar` - instance showChar :: Show Char +``` purescript +instance showChar :: Show Char +``` -### Values -#### `charString` +## Module Data.String - charString :: Char -> String -#### `fromCharCode` +Wraps the functions of Javascript's `String` object. +A String represents a sequence of characters. +For examples and details of the underlying implementation, see [String Reference at MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String). - fromCharCode :: Number -> Char +#### `charAt` -#### `toCharCode` +``` purescript +charAt :: Number -> String -> Maybe Char +``` - toCharCode :: Char -> Number +Returns the character at the given index, if the index is within bounds. +#### `fromChar` -## Module Data.String +``` purescript +fromChar :: Char -> String +``` -### Values +Returns a string of length `1` containing the given character. -#### `charAt` +#### `singleton` + +``` purescript +singleton :: Char -> String +``` - charAt :: Number -> String -> Maybe Char +Returns a string of length `1` containing the given character. +Same as `fromChar`. #### `charCodeAt` - charCodeAt :: Number -> String -> Maybe Number +``` purescript +charCodeAt :: Number -> String -> Maybe Number +``` -#### `count` +Returns the numeric Unicode value of the character at the given index, +if the index is within bounds. - count :: (Char -> Boolean) -> String -> Number +#### `null` -#### `drop` +``` purescript +null :: String -> Boolean +``` - drop :: Number -> String -> String +Returns `true` if the given string is empty. -#### `dropWhile` +#### `uncons` - dropWhile :: (Char -> Boolean) -> String -> String +``` purescript +uncons :: String -> Maybe { tail :: String, head :: Char } +``` -#### `fromChar` +Returns the first character and the rest of the string, +if the string is not empty. - fromChar :: Char -> String +#### `takeWhile` + +``` purescript +takeWhile :: (Char -> Boolean) -> String -> String +``` + +Returns the longest prefix (possibly empty) of characters that satisfy the +predicate: + +#### `dropWhile` + +``` purescript +dropWhile :: (Char -> Boolean) -> String -> String +``` + +Returns the suffix remaining after `takeWhile`. #### `fromCharArray` - fromCharArray :: [Char] -> String +``` purescript +fromCharArray :: [Char] -> String +``` + +Converts an array of characters into a string. #### `indexOf` - indexOf :: String -> String -> Number +``` purescript +indexOf :: String -> String -> Number +``` -#### `indexOf'` +Returns the index of the first occurrence of the first string in the +second string. Returns `-1` if there is no match. - indexOf' :: String -> Number -> String -> Number +#### `indexOf'` -#### `joinWith` +``` purescript +indexOf' :: String -> Number -> String -> Number +``` - joinWith :: String -> [String] -> String +Returns the index of the first occurrence of the first string in the +second string, starting at the given index. Returns `-1` if there is +no match. #### `lastIndexOf` - lastIndexOf :: String -> String -> Number +``` purescript +lastIndexOf :: String -> String -> Number +``` + +Returns the index of the last occurrence of the first string in the +second string. Returns `-1` if there is no match. #### `lastIndexOf'` - lastIndexOf' :: String -> Number -> String -> Number +``` purescript +lastIndexOf' :: String -> Number -> String -> Number +``` + +Returns the index of the first occurrence of the last string in the +second string, starting at the given index. Returns `-1` if there is +no match. #### `length` - length :: String -> Number +``` purescript +length :: String -> Number +``` -#### `localeCompare` +Returns the number of characters the string is composed of. - localeCompare :: String -> String -> Number +#### `localeCompare` -#### `null` +``` purescript +localeCompare :: String -> String -> Number +``` - null :: String -> Boolean +Locale-aware sort order comparison. Returns a negative number if the +first string occurs before the second in a sort, a positive number +if the first string occurs after the second, and 0 if they occur at the same level. #### `replace` - replace :: String -> String -> String -> String +``` purescript +replace :: String -> String -> String -> String +``` -#### `singleton` +Replaces the first occurence of the first argument with the second argument. - singleton :: Char -> String +#### `take` -#### `split` +``` purescript +take :: Number -> String -> String +``` - split :: String -> String -> [String] +Returns the first `n` characters of the string. -#### `take` +#### `drop` - take :: Number -> String -> String +``` purescript +drop :: Number -> String -> String +``` -#### `takeWhile` +Returns the string without the first `n` characters. + +#### `count` + +``` purescript +count :: (Char -> Boolean) -> String -> Number +``` - takeWhile :: (Char -> Boolean) -> String -> String +Returns the number of characters in the string for which the predicate holds. + +#### `split` + +``` purescript +split :: String -> String -> [String] +``` + +Returns the substrings of the first string separated along occurences +of the second string. #### `toCharArray` - toCharArray :: String -> [Char] +``` purescript +toCharArray :: String -> [Char] +``` + +Converts the string into an array of characters. #### `toLower` - toLower :: String -> String +``` purescript +toLower :: String -> String +``` + +Returns the argument converted to lowercase. #### `toUpper` - toUpper :: String -> String +``` purescript +toUpper :: String -> String +``` + +Returns the argument converted to uppercase. #### `trim` - trim :: String -> String +``` purescript +trim :: String -> String +``` -#### `uncons` +Removes whitespace from the beginning and end of a string, where +whitespace means all the whitespace characters (space, tab, no-break +space, etc.) and all the line terminator characters (LF, CR, etc.). - uncons :: String -> Maybe { tail :: String, head :: Char } +#### `joinWith` + +``` purescript +joinWith :: String -> [String] -> String +``` + +Joins the strings in the array together, inserting the first argument +as separator between them. ## Module Data.String.Regex -### Types + +Wraps Javascript's `RegExp` object that enables matching strings with +patternes defined by regular expressions. +For examples and details of the underlying implementation, see [RegExp Reference at MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp). #### `Regex` - data Regex :: * +``` purescript +data Regex :: * +``` + +Wraps Javascript `RegExp` objects. + +#### `showRegex` + +``` purescript +instance showRegex :: Show Regex +``` + #### `RegexFlags` - type RegexFlags = { unicode :: Boolean, sticky :: Boolean, multiline :: Boolean, ignoreCase :: Boolean, global :: Boolean } +``` purescript +type RegexFlags = { unicode :: Boolean, sticky :: Boolean, multiline :: Boolean, ignoreCase :: Boolean, global :: Boolean } +``` +Flags that control matching. -### Type Class Instances +#### `noFlags` -#### `showRegex` +``` purescript +noFlags :: RegexFlags +``` - instance showRegex :: Show Regex +All flags set to false. + +#### `regex` + +``` purescript +regex :: String -> RegexFlags -> Regex +``` + +Constructs a `Regex` from a pattern string and flags. + +#### `source` +``` purescript +source :: Regex -> String +``` -### Values +Returns the pattern string used to construct the given `Regex`. #### `flags` - flags :: Regex -> RegexFlags +``` purescript +flags :: Regex -> RegexFlags +``` -#### `match` +Returns the `RegexFlags` used to construct the given `Regex`. - match :: Regex -> String -> Maybe [String] +#### `renderFlags` -#### `noFlags` +``` purescript +renderFlags :: RegexFlags -> String +``` - noFlags :: RegexFlags +Returns the string representation of the given `RegexFlags`. #### `parseFlags` - parseFlags :: String -> RegexFlags +``` purescript +parseFlags :: String -> RegexFlags +``` -#### `regex` +Parses the string representation of `RegexFlags`. - regex :: String -> RegexFlags -> Regex +#### `test` -#### `renderFlags` +``` purescript +test :: Regex -> String -> Boolean +``` - renderFlags :: RegexFlags -> String +Returns `true` if the `Regex` matches the string. + +#### `match` + +``` purescript +match :: Regex -> String -> Maybe [String] +``` + +Matches the string against the `Regex` and returns an array of matches +if there were any. +See [reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match). #### `replace` - replace :: Regex -> String -> String -> String +``` purescript +replace :: Regex -> String -> String -> String +``` + +Replaces occurences of the `Regex` with the first string. The replacement +string can include special replacement patterns escaped with `"$"`. +See [reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace). #### `replace'` - replace' :: Regex -> (String -> [String] -> String) -> String -> String +``` purescript +replace' :: Regex -> (String -> [String] -> String) -> String -> String +``` -#### `search` +Transforms occurences of the `Regex` using a function of the matched +substring and a list of submatch strings. +See the [reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_function_as_a_parameter). - search :: Regex -> String -> Number +#### `search` -#### `source` +``` purescript +search :: Regex -> String -> Number +``` - source :: Regex -> String +Returns the index of the first match of the `Regex` in the string, or +`-1` if there is no match. #### `split` - split :: Regex -> String -> [String] - -#### `test` +``` purescript +split :: Regex -> String -> [String] +``` - test :: Regex -> String -> Boolean +Split the string into an array of substrings along occurences of the `Regex`. ## Module Data.String.Unsafe -### Values + +Unsafe string and character functions. + +#### `charCodeAt` + +``` purescript +charCodeAt :: Number -> String -> Number +``` + +Returns the numeric Unicode value of the character at the given index. + +**Unsafe:** returns `NaN` if the index is out of bounds. #### `charAt` - charAt :: Number -> String -> Char +``` purescript +charAt :: Number -> String -> Char +``` + +Returns the character at the given index. + +**Unsafe:** returns an illegal value if the index is out of bounds. + -#### `charCodeAt` - charCodeAt :: Number -> String -> Number \ No newline at end of file diff --git a/src/Data/Char/Char.purs b/src/Data/Char/Char.purs index 2fa34e3..f9034bc 100644 --- a/src/Data/Char/Char.purs +++ b/src/Data/Char/Char.purs @@ -1,3 +1,4 @@ +-- | A type and functions for single characters. module Data.Char ( Char(), charString, @@ -5,11 +6,14 @@ module Data.Char toCharCode ) where + --| A unicode character. newtype Char = Char String + -- | Returns the string of length `1` containing only the given character. charString :: Char -> String charString (Char s) = s + -- | Returns the numeric Unicode value of the character. foreign import toCharCode """ function toCharCode(c) { @@ -17,6 +21,7 @@ module Data.Char } """ :: Char -> Number + -- | Constructs a character from the given Unicode numeric value. foreign import fromCharCode """ function fromCharCode(c) { diff --git a/src/Data/String.purs b/src/Data/String.purs index 09e2714..20c93fe 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -1,3 +1,6 @@ +-- | Wraps the functions of Javascript's `String` object. +-- | A String represents a sequence of characters. +-- | For examples and details of the underlying implementation, see [String Reference at MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String). module Data.String ( charAt, @@ -39,12 +42,16 @@ module Data.String } """ :: forall a. Fn4 Number String (a -> Maybe a) (Maybe a) (Maybe Char) + -- | Returns the character at the given index, if the index is within bounds. charAt :: Number -> String -> Maybe Char charAt n s = runFn4 _charAt n s Just Nothing + -- | Returns a string of length `1` containing the given character. fromChar :: Char -> String fromChar = charString + -- | Returns a string of length `1` containing the given character. + -- | Same as `fromChar`. singleton :: Char -> String singleton = fromChar @@ -55,22 +62,31 @@ module Data.String } """ :: forall a. Fn4 Number String (a -> Maybe a) (Maybe a) (Maybe Number) + -- | Returns the numeric Unicode value of the character at the given index, + -- | if the index is within bounds. charCodeAt :: Number -> String -> Maybe Number charCodeAt n s = runFn4 _charCodeAt n s Just Nothing + -- | Returns `true` if the given string is empty. null :: String -> Boolean null s = length s == 0 + -- | Returns the first character and the rest of the string, + -- | if the string is not empty. uncons :: String -> Maybe {head :: Char, tail :: String} uncons s | null s = Nothing uncons s = Just {head : U.charAt 0 s, tail : drop 1 s} + -- | Returns the longest prefix (possibly empty) of characters that satisfy the + -- | predicate: takeWhile :: (Char -> Boolean) -> String -> String takeWhile p s = take (count p s) s + -- | Returns the suffix remaining after `takeWhile`. dropWhile :: (Char -> Boolean) -> String -> String dropWhile p s = drop (count p s) s + -- | Converts an array of characters into a string. foreign import fromCharArray """ function fromCharArray(a) { @@ -78,6 +94,8 @@ module Data.String } """ :: [Char] -> String + -- | Returns the index of the first occurrence of the first string in the + -- | second string. Returns `-1` if there is no match. foreign import indexOf """ function indexOf(x) { @@ -87,6 +105,9 @@ module Data.String } """ :: String -> String -> Number + -- | Returns the index of the first occurrence of the first string in the + -- | second string, starting at the given index. Returns `-1` if there is + -- | no match. foreign import indexOf' """ function indexOf$prime(x) { @@ -98,6 +119,8 @@ module Data.String } """ :: String -> Number -> String -> Number + -- | Returns the index of the last occurrence of the first string in the + -- | second string. Returns `-1` if there is no match. foreign import lastIndexOf """ function lastIndexOf(x) { @@ -107,6 +130,9 @@ module Data.String } """ :: String -> String -> Number + -- | Returns the index of the first occurrence of the last string in the + -- | second string, starting at the given index. Returns `-1` if there is + -- | no match. foreign import lastIndexOf' """ function lastIndexOf$prime(x) { @@ -118,6 +144,7 @@ module Data.String } """ :: String -> Number -> String -> Number + -- | Returns the number of characters the string is composed of. foreign import length """ function length(s) { @@ -125,6 +152,9 @@ module Data.String } """ :: String -> Number + -- | Locale-aware sort order comparison. Returns a negative number if the + -- | first string occurs before the second in a sort, a positive number + -- | if the first string occurs after the second, and 0 if they occur at the same level. foreign import localeCompare """ function localeCompare(s1) { @@ -134,6 +164,7 @@ module Data.String } """ :: String -> String -> Number + -- | Replaces the first occurence of the first argument with the second argument. foreign import replace """ function replace(s1) { @@ -145,6 +176,7 @@ module Data.String } """ :: String -> String -> String -> String + -- | Returns the first `n` characters of the string. foreign import take """ function take(n) { @@ -154,6 +186,7 @@ module Data.String } """ :: Number -> String -> String + -- | Returns the string without the first `n` characters. foreign import drop """ function drop(n) { @@ -163,17 +196,20 @@ module Data.String } """ :: Number -> String -> String + -- | Returns the number of characters in the string for which the predicate holds. foreign import count """ - function count(p){ + function count(p){ return function(s){ var i; for(i = 0; i < s.length && p(s.charAt(i)); i++){}; return i; - }; + }; } """ :: (Char -> Boolean) -> String -> Number + -- | Returns the substrings of the first string separated along occurences + -- | of the second string. foreign import split """ function split(sep) { @@ -183,6 +219,7 @@ module Data.String } """ :: String -> String -> [String] + -- | Converts the string into an array of characters. foreign import toCharArray """ function toCharArray(s) { @@ -190,6 +227,7 @@ module Data.String } """ :: String -> [Char] + -- | Returns the argument converted to lowercase. foreign import toLower """ function toLower(s) { @@ -197,6 +235,7 @@ module Data.String } """ :: String -> String + -- | Returns the argument converted to uppercase. foreign import toUpper """ function toUpper(s) { @@ -204,6 +243,9 @@ module Data.String } """ :: String -> String + -- | Removes whitespace from the beginning and end of a string, where + -- | whitespace means all the whitespace characters (space, tab, no-break + -- | space, etc.) and all the line terminator characters (LF, CR, etc.). foreign import trim """ function trim(s) { @@ -211,6 +253,8 @@ module Data.String } """ :: String -> String + -- | Joins the strings in the array together, inserting the first argument + -- | as separator between them. foreign import joinWith """ function joinWith(s) { diff --git a/src/Data/String/Regex.purs b/src/Data/String/Regex.purs index 6ad1bce..f3daf42 100644 --- a/src/Data/String/Regex.purs +++ b/src/Data/String/Regex.purs @@ -1,3 +1,6 @@ +-- | Wraps Javascript's `RegExp` object that enables matching strings with +-- | patternes defined by regular expressions. +-- | For examples and details of the underlying implementation, see [RegExp Reference at MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp). module Data.String.Regex ( Regex(..) , RegexFlags(..) @@ -19,6 +22,7 @@ import Data.Function import Data.Maybe import Data.String (indexOf) +-- | Wraps Javascript `RegExp` objects. foreign import data Regex :: * foreign import showRegex' @@ -31,6 +35,7 @@ foreign import showRegex' instance showRegex :: Show Regex where show = showRegex' +-- | Flags that control matching. type RegexFlags = { global :: Boolean , ignoreCase :: Boolean @@ -39,6 +44,7 @@ type RegexFlags = , unicode :: Boolean } +-- | All flags set to false. noFlags :: RegexFlags noFlags = { global : false , ignoreCase : false @@ -55,9 +61,11 @@ foreign import regex' } """ :: String -> String -> Regex +-- | Constructs a `Regex` from a pattern string and flags. regex :: String -> RegexFlags -> Regex regex source flags = regex' source $ renderFlags flags +-- | Returns the pattern string used to construct the given `Regex`. foreign import source """ function source(r) { @@ -65,6 +73,7 @@ foreign import source } """ :: Regex -> String +-- | Returns the `RegexFlags` used to construct the given `Regex`. foreign import flags """ function flags(r) { @@ -78,6 +87,7 @@ foreign import flags } """ :: Regex -> RegexFlags +-- | Returns the string representation of the given `RegexFlags`. renderFlags :: RegexFlags -> String renderFlags flags = (if flags.global then "g" else "") ++ @@ -86,6 +96,7 @@ renderFlags flags = (if flags.sticky then "y" else "") ++ (if flags.unicode then "u" else "") +-- | Parses the string representation of `RegexFlags`. parseFlags :: String -> RegexFlags parseFlags s = { global: indexOf "g" s >= 0 @@ -95,6 +106,7 @@ parseFlags s = , unicode: indexOf "u" s >= 0 } +-- | Returns `true` if the `Regex` matches the string. foreign import test """ function test(r) { @@ -112,9 +124,15 @@ foreign import _match } """ :: forall r. Fn4 Regex String ([String] -> r) r r +-- | Matches the string against the `Regex` and returns an array of matches +-- | if there were any. +-- | See [reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match). match :: Regex -> String -> Maybe [String] match r s = runFn4 _match r s Just Nothing +-- | Replaces occurences of the `Regex` with the first string. The replacement +-- | string can include special replacement patterns escaped with `"$"`. +-- | See [reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace). foreign import replace """ function replace(r) { @@ -126,6 +144,9 @@ foreign import replace } """ :: Regex -> String -> String -> String +-- | Transforms occurences of the `Regex` using a function of the matched +-- | substring and a list of submatch strings. +-- | 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' """ function replace$prime(r) { @@ -139,6 +160,8 @@ foreign import replace' } """ :: Regex -> (String -> [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 """ function search(r) { @@ -148,6 +171,7 @@ foreign import search } """ :: Regex -> String -> Number +-- | Split the string into an array of substrings along occurences of the `Regex`. foreign import split """ function split(r) { diff --git a/src/Data/String/Unsafe.purs b/src/Data/String/Unsafe.purs index 20e54dc..c31cba7 100644 --- a/src/Data/String/Unsafe.purs +++ b/src/Data/String/Unsafe.purs @@ -1,3 +1,4 @@ +-- | Unsafe string and character functions. module Data.String.Unsafe ( charAt , charCodeAt @@ -5,6 +6,9 @@ module Data.String.Unsafe import Data.Char + -- | Returns the numeric Unicode value of the character at the given index. + -- | + -- | **Unsafe:** returns `NaN` if the index is out of bounds. foreign import charCodeAt """ function charCodeAt(i) { @@ -14,6 +18,9 @@ module Data.String.Unsafe } """ :: Number -> String -> Number + -- | Returns the character at the given index. + -- | + -- | **Unsafe:** returns an illegal value if the index is out of bounds. foreign import charAt """ function charAt(i) { From 0316afb3b291f290ce8f80b19c7746ae72dfe382 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Boros?= Date: Sat, 21 Mar 2015 18:41:23 +0100 Subject: [PATCH 006/186] cleanup, add instance doc --- README.md | 20 ++++++++++++-------- src/Data/Char/Char.purs | 3 +++ src/Data/String.purs | 15 ++++++++------- src/Data/String/Regex.purs | 2 +- 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 8052e4e..31504b7 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ Constructs a character from the given Unicode numeric value. instance eqChar :: Eq Char ``` +Characters can be compared for equality with `==` and `/=`. #### `ordChar` @@ -50,6 +51,7 @@ instance eqChar :: Eq Char instance ordChar :: Ord Char ``` +Characters can be compared with `compare`, `>`, `>=`, `<` and `<=`. #### `showChar` @@ -57,6 +59,7 @@ instance ordChar :: Ord Char instance showChar :: Show Char ``` +Characters can be rendered as a string with `show`. ## Module Data.String @@ -64,7 +67,7 @@ instance showChar :: Show Char Wraps the functions of Javascript's `String` object. A String represents a sequence of characters. -For examples and details of the underlying implementation, see [String Reference at MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String). +For details of the underlying implementation, see [String Reference at MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String). #### `charAt` @@ -123,8 +126,8 @@ if the string is not empty. takeWhile :: (Char -> Boolean) -> String -> String ``` -Returns the longest prefix (possibly empty) of characters that satisfy the -predicate: +Returns the longest prefix (possibly empty) of characters that satisfy +the predicate: #### `dropWhile` @@ -196,7 +199,8 @@ localeCompare :: String -> String -> Number Locale-aware sort order comparison. Returns a negative number if the first string occurs before the second in a sort, a positive number -if the first string occurs after the second, and 0 if they occur at the same level. +if the first string occurs after the second, and `0` if their sort order +is equal. #### `replace` @@ -269,9 +273,9 @@ Returns the argument converted to uppercase. trim :: String -> String ``` -Removes whitespace from the beginning and end of a string, where -whitespace means all the whitespace characters (space, tab, no-break -space, etc.) and all the line terminator characters (LF, CR, etc.). +Removes whitespace from the beginning and end of a string, including +[whitespace characters](http://www.ecma-international.org/ecma-262/5.1/#sec-7.2) +and [line terminators](http://www.ecma-international.org/ecma-262/5.1/#sec-7.3). #### `joinWith` @@ -288,7 +292,7 @@ as separator between them. Wraps Javascript's `RegExp` object that enables matching strings with patternes defined by regular expressions. -For examples and details of the underlying implementation, see [RegExp Reference at MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp). +For details of the underlying implementation, see [RegExp Reference at MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp). #### `Regex` diff --git a/src/Data/Char/Char.purs b/src/Data/Char/Char.purs index f9034bc..a243607 100644 --- a/src/Data/Char/Char.purs +++ b/src/Data/Char/Char.purs @@ -29,13 +29,16 @@ module Data.Char } """ :: Number -> Char + -- | Characters can be compared for equality with `==` and `/=`. instance eqChar :: Eq Char where (==) (Char a) (Char b) = a == b (/=) a b = not (a == b) + -- | Characters can be compared with `compare`, `>`, `>=`, `<` and `<=`. instance ordChar :: Ord Char where compare (Char a) (Char b) = a `compare` b + -- | Characters can be rendered as a string with `show`. instance showChar :: Show Char where show (Char s) = "Char " ++ show s diff --git a/src/Data/String.purs b/src/Data/String.purs index 20c93fe..8b31d4d 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -1,6 +1,6 @@ -- | Wraps the functions of Javascript's `String` object. -- | A String represents a sequence of characters. --- | For examples and details of the underlying implementation, see [String Reference at MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String). +-- | For details of the underlying implementation, see [String Reference at MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String). module Data.String ( charAt, @@ -77,8 +77,8 @@ module Data.String uncons s | null s = Nothing uncons s = Just {head : U.charAt 0 s, tail : drop 1 s} - -- | Returns the longest prefix (possibly empty) of characters that satisfy the - -- | predicate: + -- | Returns the longest prefix (possibly empty) of characters that satisfy + -- | the predicate: takeWhile :: (Char -> Boolean) -> String -> String takeWhile p s = take (count p s) s @@ -154,7 +154,8 @@ module Data.String -- | Locale-aware sort order comparison. Returns a negative number if the -- | first string occurs before the second in a sort, a positive number - -- | if the first string occurs after the second, and 0 if they occur at the same level. + -- | if the first string occurs after the second, and `0` if their sort order + -- | is equal. foreign import localeCompare """ function localeCompare(s1) { @@ -243,9 +244,9 @@ module Data.String } """ :: String -> String - -- | Removes whitespace from the beginning and end of a string, where - -- | whitespace means all the whitespace characters (space, tab, no-break - -- | space, etc.) and all the line terminator characters (LF, CR, etc.). + -- | Removes whitespace from the beginning and end of a string, including + -- | [whitespace characters](http://www.ecma-international.org/ecma-262/5.1/#sec-7.2) + -- | and [line terminators](http://www.ecma-international.org/ecma-262/5.1/#sec-7.3). foreign import trim """ function trim(s) { diff --git a/src/Data/String/Regex.purs b/src/Data/String/Regex.purs index f3daf42..8a06310 100644 --- a/src/Data/String/Regex.purs +++ b/src/Data/String/Regex.purs @@ -1,6 +1,6 @@ -- | Wraps Javascript's `RegExp` object that enables matching strings with -- | patternes defined by regular expressions. --- | For examples and details of the underlying implementation, see [RegExp Reference at MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp). +-- | For details of the underlying implementation, see [RegExp Reference at MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp). module Data.String.Regex ( Regex(..) , RegexFlags(..) From 52b5916518c286677e234d0bb1bad3a57b05fc23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Boros?= Date: Sun, 22 Mar 2015 04:45:42 +0100 Subject: [PATCH 007/186] improve Unsafe --- README.md | 14 ++++++++++++-- src/Data/String/Unsafe.purs | 26 +++++++++++++++++++++++--- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 31504b7..3cbbacc 100644 --- a/README.md +++ b/README.md @@ -434,7 +434,7 @@ charCodeAt :: Number -> String -> Number Returns the numeric Unicode value of the character at the given index. -**Unsafe:** returns `NaN` if the index is out of bounds. +**Unsafe:** throws runtime exception if the index is out of bounds. #### `charAt` @@ -444,7 +444,17 @@ charAt :: Number -> String -> Char Returns the character at the given index. -**Unsafe:** returns an illegal value if the index is out of bounds. +**Unsafe:** throws runtime exception if the index is out of bounds. + +#### `char` + +``` purescript +char :: String -> Char +``` + +Converts a string of length `1` to a character.. + +**Unsafe:** throws runtime exception if length is not `1`. diff --git a/src/Data/String/Unsafe.purs b/src/Data/String/Unsafe.purs index c31cba7..18f6bd4 100644 --- a/src/Data/String/Unsafe.purs +++ b/src/Data/String/Unsafe.purs @@ -1,6 +1,7 @@ -- | Unsafe string and character functions. module Data.String.Unsafe - ( charAt + ( char + , charAt , charCodeAt ) where @@ -8,11 +9,14 @@ module Data.String.Unsafe -- | Returns the numeric Unicode value of the character at the given index. -- | - -- | **Unsafe:** returns `NaN` if the index is out of bounds. + -- | **Unsafe:** throws runtime exception if the index is out of bounds. foreign import charCodeAt """ function charCodeAt(i) { return function(s) { + if (s.length <= i) { + throw new Error("Data.String.Unsafe.charCodeAt: Invalid index."); + }; return s.charCodeAt(i); }; } @@ -20,12 +24,28 @@ module Data.String.Unsafe -- | Returns the character at the given index. -- | - -- | **Unsafe:** returns an illegal value if the index is out of bounds. + -- | **Unsafe:** throws runtime exception if the index is out of bounds. foreign import charAt """ function charAt(i) { return function(s) { + if (s.length <= i) { + throw new Error("Data.String.Unsafe.charAt: Invalid index."); + }; return s.charAt(i); }; } """ :: Number -> String -> Char + + -- | Converts a string of length `1` to a character.. + -- | + -- | **Unsafe:** throws runtime exception if length is not `1`. + foreign import char + """ + function $$char(s) { + if (s.length != 1) { + throw new Error("Data.String.Unsafe.char: Expected string of length 1."); + }; + return s.charAt(0); + } + """ :: String -> Char From 7445c05bd0466173961361a008e48e7659cc5897 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Boros?= Date: Sun, 22 Mar 2015 06:34:55 +0100 Subject: [PATCH 008/186] fix typo --- README.md | 2 +- src/Data/String/Unsafe.purs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3cbbacc..4545e05 100644 --- a/README.md +++ b/README.md @@ -452,7 +452,7 @@ Returns the character at the given index. char :: String -> Char ``` -Converts a string of length `1` to a character.. +Converts a string of length `1` to a character. **Unsafe:** throws runtime exception if length is not `1`. diff --git a/src/Data/String/Unsafe.purs b/src/Data/String/Unsafe.purs index 18f6bd4..547f002 100644 --- a/src/Data/String/Unsafe.purs +++ b/src/Data/String/Unsafe.purs @@ -37,7 +37,7 @@ module Data.String.Unsafe } """ :: Number -> String -> Char - -- | Converts a string of length `1` to a character.. + -- | Converts a string of length `1` to a character. -- | -- | **Unsafe:** throws runtime exception if length is not `1`. foreign import char From 9905107c5cf2177d019bd825fc5e073762833560 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Tue, 7 Apr 2015 23:01:49 +0100 Subject: [PATCH 009/186] Add Bounded instance --- README.md | 12 ++++-- src/Data/Char/Char.purs | 84 +++++++++++++++++++++-------------------- 2 files changed, 52 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 4545e05..2510618 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,13 @@ instance ordChar :: Ord Char Characters can be compared with `compare`, `>`, `>=`, `<` and `<=`. +#### `boundedChar` + +``` purescript +instance boundedChar :: Bounded Char +``` + + #### `showChar` ``` purescript @@ -454,7 +461,4 @@ char :: String -> Char Converts a string of length `1` to a character. -**Unsafe:** throws runtime exception if length is not `1`. - - - +**Unsafe:** throws runtime exception if length is not `1`. \ No newline at end of file diff --git a/src/Data/Char/Char.purs b/src/Data/Char/Char.purs index a243607..2d98196 100644 --- a/src/Data/Char/Char.purs +++ b/src/Data/Char/Char.purs @@ -1,44 +1,48 @@ -- | A type and functions for single characters. module Data.Char - ( Char(), - charString, - fromCharCode, - toCharCode + ( Char() + , charString + , fromCharCode + , toCharCode ) where - --| A unicode character. - newtype Char = Char String - - -- | Returns the string of length `1` containing only the given character. - charString :: Char -> String - charString (Char s) = s - - -- | Returns the numeric Unicode value of the character. - foreign import toCharCode - """ - function toCharCode(c) { - return c.charCodeAt(0); - } - """ :: Char -> Number - - -- | Constructs a character from the given Unicode numeric value. - foreign import fromCharCode - """ - function fromCharCode(c) { - return String.fromCharCode(c); - } - """ :: Number -> Char - - -- | Characters can be compared for equality with `==` and `/=`. - instance eqChar :: Eq Char where - (==) (Char a) (Char b) = a == b - - (/=) a b = not (a == b) - - -- | Characters can be compared with `compare`, `>`, `>=`, `<` and `<=`. - instance ordChar :: Ord Char where - compare (Char a) (Char b) = a `compare` b - - -- | Characters can be rendered as a string with `show`. - instance showChar :: Show Char where - show (Char s) = "Char " ++ show s +--| A unicode character. +newtype Char = Char String + +-- | Returns the string of length `1` containing only the given character. +charString :: Char -> String +charString (Char s) = s + +-- | Returns the numeric Unicode value of the character. +foreign import toCharCode + """ + function toCharCode(c) { + return c.charCodeAt(0); + } + """ :: Char -> Number + +-- | Constructs a character from the given Unicode numeric value. +foreign import fromCharCode + """ + function fromCharCode(c) { + return String.fromCharCode(c); + } + """ :: Number -> Char + +-- | Characters can be compared for equality with `==` and `/=`. +instance eqChar :: Eq Char where + (==) (Char a) (Char b) = a == b + + (/=) a b = not (a == b) + +-- | Characters can be compared with `compare`, `>`, `>=`, `<` and `<=`. +instance ordChar :: Ord Char where + compare (Char a) (Char b) = a `compare` b + +instance boundedChar :: Bounded Char where + top = fromCharCode 0 + bottom = fromCharCode 65535 + +-- | Characters can be rendered as a string with `show`. +instance showChar :: Show Char where + show (Char s) = "Char " ++ show s From 9d8a554c5fe72e4d0abddae9de86bfae8a2ed581 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Tue, 7 Apr 2015 23:12:29 +0100 Subject: [PATCH 010/186] Use Int for char codes --- README.md | 4 ++-- bower.json | 3 ++- src/Data/Char/Char.purs | 11 ++++++----- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 2510618..812016a 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Returns the string of length `1` containing only the given character. #### `toCharCode` ``` purescript -toCharCode :: Char -> Number +toCharCode :: Char -> Int ``` Returns the numeric Unicode value of the character. @@ -32,7 +32,7 @@ Returns the numeric Unicode value of the character. #### `fromCharCode` ``` purescript -fromCharCode :: Number -> Char +fromCharCode :: Int -> Char ``` Constructs a character from the given Unicode numeric value. diff --git a/bower.json b/bower.json index de4b307..855ef66 100644 --- a/bower.json +++ b/bower.json @@ -18,6 +18,7 @@ "package.json" ], "dependencies": { - "purescript-maybe": "~0.2.1" + "purescript-maybe": "~0.2.1", + "purescript-integers": "~0.1.0" } } diff --git a/src/Data/Char/Char.purs b/src/Data/Char/Char.purs index 2d98196..902be72 100644 --- a/src/Data/Char/Char.purs +++ b/src/Data/Char/Char.purs @@ -6,6 +6,8 @@ module Data.Char , toCharCode ) where +import Data.Int (Int(), fromNumber) + --| A unicode character. newtype Char = Char String @@ -19,7 +21,7 @@ foreign import toCharCode function toCharCode(c) { return c.charCodeAt(0); } - """ :: Char -> Number + """ :: Char -> Int -- | Constructs a character from the given Unicode numeric value. foreign import fromCharCode @@ -27,12 +29,11 @@ foreign import fromCharCode function fromCharCode(c) { return String.fromCharCode(c); } - """ :: Number -> Char + """ :: Int -> Char -- | Characters can be compared for equality with `==` and `/=`. instance eqChar :: Eq Char where (==) (Char a) (Char b) = a == b - (/=) a b = not (a == b) -- | Characters can be compared with `compare`, `>`, `>=`, `<` and `<=`. @@ -40,8 +41,8 @@ instance ordChar :: Ord Char where compare (Char a) (Char b) = a `compare` b instance boundedChar :: Bounded Char where - top = fromCharCode 0 - bottom = fromCharCode 65535 + top = fromCharCode zero + bottom = fromCharCode (fromNumber 65535) -- | Characters can be rendered as a string with `show`. instance showChar :: Show Char where From 76ddec3fc29ca92a99312b921aca7715db97e7aa Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Sat, 11 Apr 2015 16:53:39 +0100 Subject: [PATCH 011/186] Use Int everywhere, add contains, rename charString --- README.md | 49 ++-- src/Data/Char/Char.purs | 5 +- src/Data/String.purs | 525 ++++++++++++++++++------------------ src/Data/String/Regex.purs | 17 +- src/Data/String/Unsafe.purs | 77 +++--- 5 files changed, 341 insertions(+), 332 deletions(-) diff --git a/README.md b/README.md index 812016a..12731a4 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ For details of the underlying implementation, see [String Reference at MDN](http #### `charAt` ``` purescript -charAt :: Number -> String -> Maybe Char +charAt :: Int -> String -> Maybe Char ``` Returns the character at the given index, if the index is within bounds. @@ -104,7 +104,7 @@ Same as `fromChar`. #### `charCodeAt` ``` purescript -charCodeAt :: Number -> String -> Maybe Number +charCodeAt :: Int -> String -> Maybe Int ``` Returns the numeric Unicode value of the character at the given index, @@ -152,29 +152,37 @@ fromCharArray :: [Char] -> String Converts an array of characters into a string. +#### `contains` + +``` purescript +contains :: String -> String -> Boolean +``` + +Checks whether the first string exists in the second string. + #### `indexOf` ``` purescript -indexOf :: String -> String -> Number +indexOf :: String -> String -> Maybe Int ``` Returns the index of the first occurrence of the first string in the -second string. Returns `-1` if there is no match. +second string. Returns `Nothing` if there is no match. #### `indexOf'` ``` purescript -indexOf' :: String -> Number -> String -> Number +indexOf' :: String -> Int -> String -> Maybe Int ``` Returns the index of the first occurrence of the first string in the -second string, starting at the given index. Returns `-1` if there is +second string, starting at the given index. Returns `Nothing` if there is no match. #### `lastIndexOf` ``` purescript -lastIndexOf :: String -> String -> Number +lastIndexOf :: String -> String -> Maybe Int ``` Returns the index of the last occurrence of the first string in the @@ -183,17 +191,17 @@ second string. Returns `-1` if there is no match. #### `lastIndexOf'` ``` purescript -lastIndexOf' :: String -> Number -> String -> Number +lastIndexOf' :: String -> Int -> String -> Maybe Int ``` -Returns the index of the first occurrence of the last string in the -second string, starting at the given index. Returns `-1` if there is +Returns the index of the last occurrence of the first string in the +second string, starting at the given index. Returns `Nothing` if there is no match. #### `length` ``` purescript -length :: String -> Number +length :: String -> Int ``` Returns the number of characters the string is composed of. @@ -201,13 +209,10 @@ Returns the number of characters the string is composed of. #### `localeCompare` ``` purescript -localeCompare :: String -> String -> Number +localeCompare :: String -> String -> Ordering ``` -Locale-aware sort order comparison. Returns a negative number if the -first string occurs before the second in a sort, a positive number -if the first string occurs after the second, and `0` if their sort order -is equal. +Locale-aware sort order comparison. #### `replace` @@ -220,7 +225,7 @@ Replaces the first occurence of the first argument with the second argument. #### `take` ``` purescript -take :: Number -> String -> String +take :: Int -> String -> String ``` Returns the first `n` characters of the string. @@ -228,7 +233,7 @@ Returns the first `n` characters of the string. #### `drop` ``` purescript -drop :: Number -> String -> String +drop :: Int -> String -> String ``` Returns the string without the first `n` characters. @@ -236,7 +241,7 @@ Returns the string without the first `n` characters. #### `count` ``` purescript -count :: (Char -> Boolean) -> String -> Number +count :: (Char -> Boolean) -> String -> Int ``` Returns the number of characters in the string for which the predicate holds. @@ -413,7 +418,7 @@ See the [reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe #### `search` ``` purescript -search :: Regex -> String -> Number +search :: Regex -> String -> Int ``` Returns the index of the first match of the `Regex` in the string, or @@ -436,7 +441,7 @@ Unsafe string and character functions. #### `charCodeAt` ``` purescript -charCodeAt :: Number -> String -> Number +charCodeAt :: Int -> String -> Int ``` Returns the numeric Unicode value of the character at the given index. @@ -446,7 +451,7 @@ Returns the numeric Unicode value of the character at the given index. #### `charAt` ``` purescript -charAt :: Number -> String -> Char +charAt :: Int -> String -> Char ``` Returns the character at the given index. diff --git a/src/Data/Char/Char.purs b/src/Data/Char/Char.purs index 902be72..16bd885 100644 --- a/src/Data/Char/Char.purs +++ b/src/Data/Char/Char.purs @@ -12,8 +12,8 @@ import Data.Int (Int(), fromNumber) newtype Char = Char String -- | Returns the string of length `1` containing only the given character. -charString :: Char -> String -charString (Char s) = s +toString :: Char -> String +toString (Char s) = s -- | Returns the numeric Unicode value of the character. foreign import toCharCode @@ -40,6 +40,7 @@ instance eqChar :: Eq Char where instance ordChar :: Ord Char where compare (Char a) (Char b) = a `compare` b +-- | Characters fall within the Unicode range. instance boundedChar :: Bounded Char where top = fromCharCode zero bottom = fromCharCode (fromNumber 65535) diff --git a/src/Data/String.purs b/src/Data/String.purs index 8b31d4d..fe79ccc 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -2,265 +2,272 @@ -- | A String represents a sequence of characters. -- | For details of the underlying implementation, see [String Reference at MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String). module Data.String - ( - charAt, - charCodeAt, - fromCharArray, - fromChar, - indexOf, - indexOf', - lastIndexOf, - lastIndexOf', - null, - uncons, - length, - singleton, - localeCompare, - replace, - count, - take, - takeWhile, - drop, - dropWhile, - split, - toCharArray, - toLower, - toUpper, - trim, - joinWith + ( charAt + , charCodeAt + , fromCharArray + , fromChar + , contains + , indexOf + , indexOf' + , lastIndexOf + , lastIndexOf' + , null + , uncons + , length + , singleton + , localeCompare + , replace + , count + , take + , takeWhile + , drop + , dropWhile + , split + , toCharArray + , toLower + , toUpper + , trim + , joinWith ) where - import Data.Maybe - import Data.Char - import Data.Function - import qualified Data.String.Unsafe as U - - foreign import _charAt - """ - function _charAt(i, s, Just, Nothing) { - return i >= 0 && i < s.length ? Just(s.charAt(i)) : Nothing; - } - """ :: forall a. Fn4 Number String (a -> Maybe a) (Maybe a) (Maybe Char) - - -- | Returns the character at the given index, if the index is within bounds. - charAt :: Number -> String -> Maybe Char - charAt n s = runFn4 _charAt n s Just Nothing - - -- | Returns a string of length `1` containing the given character. - fromChar :: Char -> String - fromChar = charString - - -- | Returns a string of length `1` containing the given character. - -- | Same as `fromChar`. - singleton :: Char -> String - singleton = fromChar - - foreign import _charCodeAt - """ - function _charCodeAt(i, s, Just, Nothing) { - return i >= 0 && i < s.length ? Just(s.charCodeAt(i)) : Nothing; - } - """ :: forall a. Fn4 Number String (a -> Maybe a) (Maybe a) (Maybe Number) - - -- | Returns the numeric Unicode value of the character at the given index, - -- | if the index is within bounds. - charCodeAt :: Number -> String -> Maybe Number - charCodeAt n s = runFn4 _charCodeAt n s Just Nothing - - -- | Returns `true` if the given string is empty. - null :: String -> Boolean - null s = length s == 0 - - -- | Returns the first character and the rest of the string, - -- | if the string is not empty. - uncons :: String -> Maybe {head :: Char, tail :: String} - uncons s | null s = Nothing - uncons s = Just {head : U.charAt 0 s, tail : drop 1 s} - - -- | Returns the longest prefix (possibly empty) of characters that satisfy - -- | the predicate: - takeWhile :: (Char -> Boolean) -> String -> String - takeWhile p s = take (count p s) s - - -- | Returns the suffix remaining after `takeWhile`. - dropWhile :: (Char -> Boolean) -> String -> String - dropWhile p s = drop (count p s) s - - -- | Converts an array of characters into a string. - foreign import fromCharArray - """ - function fromCharArray(a) { - return a.join(''); - } - """ :: [Char] -> String - - -- | Returns the index of the first occurrence of the first string in the - -- | second string. Returns `-1` if there is no match. - foreign import indexOf - """ - function indexOf(x) { - return function(s) { - return s.indexOf(x); - }; - } - """ :: String -> String -> Number - - -- | Returns the index of the first occurrence of the first string in the - -- | second string, starting at the given index. Returns `-1` if there is - -- | no match. - foreign import indexOf' - """ - function indexOf$prime(x) { - return function(startAt) { - return function(s) { - return s.indexOf(x, startAt); - }; - }; - } - """ :: String -> Number -> String -> Number - - -- | Returns the index of the last occurrence of the first string in the - -- | second string. Returns `-1` if there is no match. - foreign import lastIndexOf - """ - function lastIndexOf(x) { - return function(s) { - return s.lastIndexOf(x); - }; - } - """ :: String -> String -> Number - - -- | Returns the index of the first occurrence of the last string in the - -- | second string, starting at the given index. Returns `-1` if there is - -- | no match. - foreign import lastIndexOf' - """ - function lastIndexOf$prime(x) { - return function(startAt) { - return function(s) { - return s.lastIndexOf(x, startAt); - }; - }; - } - """ :: String -> Number -> String -> Number - - -- | Returns the number of characters the string is composed of. - foreign import length - """ - function length(s) { - return s.length; - } - """ :: String -> Number - - -- | Locale-aware sort order comparison. Returns a negative number if the - -- | first string occurs before the second in a sort, a positive number - -- | if the first string occurs after the second, and `0` if their sort order - -- | is equal. - foreign import localeCompare - """ - function localeCompare(s1) { - return function(s2) { - return s1.localeCompare(s2); - }; - } - """ :: String -> String -> Number - - -- | Replaces the first occurence of the first argument with the second argument. - foreign import replace - """ - function replace(s1) { - return function(s2) { - return function(s3) { - return s3.replace(s1, s2); - }; - }; - } - """ :: String -> String -> String -> String - - -- | Returns the first `n` characters of the string. - foreign import take - """ - function take(n) { - return function(s) { - return s.substr(0, n); - }; - } - """ :: Number -> String -> String - - -- | Returns the string without the first `n` characters. - foreign import drop - """ - function drop(n) { - return function(s) { - return s.substr(n); - }; - } - """ :: Number -> String -> String - - -- | Returns the number of characters in the string for which the predicate holds. - foreign import count - """ - function count(p){ - return function(s){ - var i; - for(i = 0; i < s.length && p(s.charAt(i)); i++){}; - return i; - }; - } - """ :: (Char -> Boolean) -> String -> Number - - -- | Returns the substrings of the first string separated along occurences - -- | of the second string. - foreign import split - """ - function split(sep) { - return function(s) { - return s.split(sep); - }; - } - """ :: String -> String -> [String] - - -- | Converts the string into an array of characters. - foreign import toCharArray - """ - function toCharArray(s) { - return s.split(''); - } - """ :: String -> [Char] - - -- | Returns the argument converted to lowercase. - foreign import toLower - """ - function toLower(s) { - return s.toLowerCase(); - } - """ :: String -> String - - -- | Returns the argument converted to uppercase. - foreign import toUpper - """ - function toUpper(s) { - return s.toUpperCase(); - } - """ :: String -> String - - -- | Removes whitespace from the beginning and end of a string, including - -- | [whitespace characters](http://www.ecma-international.org/ecma-262/5.1/#sec-7.2) - -- | and [line terminators](http://www.ecma-international.org/ecma-262/5.1/#sec-7.3). - foreign import trim - """ - function trim(s) { - return s.trim(); - } - """ :: String -> String - - -- | Joins the strings in the array together, inserting the first argument - -- | as separator between them. - foreign import joinWith - """ - function joinWith(s) { - return function(xs) { - return xs.join(s); +import Data.Int (Int()) +import Data.Maybe (Maybe(..), isJust) +import Data.Char +import Data.Function (Fn4(), runFn4, Fn5(), runFn5) +import qualified Data.String.Unsafe as U + +-- | Returns the character at the given index, if the index is within bounds. +charAt :: Int -> String -> Maybe Char +charAt n s = runFn4 _charAt n s Just Nothing + +foreign import _charAt + """ + function _charAt(i, s, Just, Nothing) { + return i >= 0 && i < s.length ? Just(s.charAt(i)) : Nothing; + } + """ :: forall a. Fn4 Int String (a -> Maybe a) (Maybe a) (Maybe Char) + +-- | Returns a string of length `1` containing the given character. +fromChar :: Char -> String +fromChar = toString + +-- | Returns a string of length `1` containing the given character. +-- | Same as `fromChar`. +singleton :: Char -> String +singleton = fromChar + +foreign import _charCodeAt + """ + function _charCodeAt(i, s, Just, Nothing) { + return i >= 0 && i < s.length ? Just(s.charCodeAt(i)) : Nothing; + } + """ :: forall a. Fn4 Int String (a -> Maybe a) (Maybe a) (Maybe Int) + +-- | Returns the numeric Unicode value of the character at the given index, +-- | if the index is within bounds. +charCodeAt :: Int -> String -> Maybe Int +charCodeAt n s = runFn4 _charCodeAt n s Just Nothing + +-- | Returns `true` if the given string is empty. +null :: String -> Boolean +null s = length s == zero + +-- | Returns the first character and the rest of the string, +-- | if the string is not empty. +uncons :: String -> Maybe { head :: Char, tail :: String } +uncons "" = Nothing +uncons s = Just { head: U.charAt zero s, tail: drop one s } + +-- | Returns the longest prefix (possibly empty) of characters that satisfy +-- | the predicate: +takeWhile :: (Char -> Boolean) -> String -> String +takeWhile p s = take (count p s) s + +-- | Returns the suffix remaining after `takeWhile`. +dropWhile :: (Char -> Boolean) -> String -> String +dropWhile p s = drop (count p s) s + +-- | Converts an array of characters into a string. +foreign import fromCharArray + """ + function fromCharArray(a) { + return a.join(''); + } + """ :: [Char] -> String + +-- | Checks whether the first string exists in the second string. +contains :: String -> String -> Boolean +contains x s = isJust (indexOf x s) + +-- | Returns the index of the first occurrence of the first string in the +-- | second string. Returns `Nothing` if there is no match. +indexOf :: String -> String -> Maybe Int +indexOf x s = runFn4 _indexOf Just Nothing x s + +foreign import _indexOf + """ + function _indexOf(just, nothing, x, s) { + var i = s.indexOf(x); + return i == -1 ? nothing : just(i); + } + """ :: forall a. Fn4 (a -> Maybe a) (Maybe a) String String (Maybe Int) + +-- | Returns the index of the first occurrence of the first string in the +-- | second string, starting at the given index. Returns `Nothing` if there is +-- | no match. +indexOf' :: String -> Int -> String -> Maybe Int +indexOf' x i s = runFn5 _indexOf' Just Nothing x i s + +foreign import _indexOf' + """ + function _indexOf$prime(just, nothing, x, startAt, s) { + var i = s.indexOf(x, startAt); + return i == -1 ? nothing : just(i); + } + """ :: forall a. Fn5 (a -> Maybe a) (Maybe a) String Int String (Maybe Int) + +-- | Returns the index of the last occurrence of the first string in the +-- | second string. Returns `-1` if there is no match. +lastIndexOf :: String -> String -> Maybe Int +lastIndexOf x s = runFn4 _lastIndexOf Just Nothing x s + +foreign import _lastIndexOf + """ + function _lastIndexOf(just, nothing, x, s) { + var i = s.lastIndexOf(x); + return i == -1 ? nothing : just(i); + } + """ :: forall a. Fn4 (a -> Maybe a) (Maybe a) String String (Maybe Int) + +-- | Returns the index of the last occurrence of the first string in the +-- | second string, starting at the given index. Returns `Nothing` if there is +-- | no match. +lastIndexOf' :: String -> Int -> String -> Maybe Int +lastIndexOf' x i s = runFn5 _lastIndexOf' Just Nothing x i s + +foreign import _lastIndexOf' + """ + function _lastIndexOf$prime(just, nothing, x, startAt, s) { + var i = s.lastIndexOf(x, startAt); + return i == -1 ? nothing : just(i); + } + """ :: forall a. Fn5 (a -> Maybe a) (Maybe a) String Int String (Maybe Int) + +-- | Returns the number of characters the string is composed of. +foreign import length + """ + function length(s) { + return s.length; + } + """ :: String -> Int + +-- | Locale-aware sort order comparison. +localeCompare :: String -> String -> Ordering +localeCompare s1 s2 = runFn5 _localeCompare LT EQ GT s1 s2 + +foreign import _localeCompare + """ + function localeCompare(lt, eq, gt, s1, s2) { + var result = s1.localeCompare(s2); + return result < 0 ? lt : result > 1 ? gt : eq; + } + """ :: Fn5 Ordering Ordering Ordering String String Ordering + +-- | Replaces the first occurence of the first argument with the second argument. +foreign import replace + """ + function replace(s1) { + return function(s2) { + return function(s3) { + return s3.replace(s1, s2); }; - } - """ :: String -> [String] -> String + }; + } + """ :: String -> String -> String -> String + +-- | Returns the first `n` characters of the string. +foreign import take + """ + function take(n) { + return function(s) { + return s.substr(0, n); + }; + } + """ :: Int -> String -> String + +-- | Returns the string without the first `n` characters. +foreign import drop + """ + function drop(n) { + return function(s) { + return s.substr(n); + }; + } + """ :: Int -> String -> String + +-- | Returns the number of characters in the string for which the predicate holds. +foreign import count + """ + function count(p){ + return function(s){ + for(var i = 0; i < s.length && p(s.charAt(i)); i++){}; + return i; + }; + } + """ :: (Char -> Boolean) -> String -> Int + +-- | Returns the substrings of the first string separated along occurences +-- | of the second string. +foreign import split + """ + function split(sep) { + return function(s) { + return s.split(sep); + }; + } + """ :: String -> String -> [String] + +-- | Converts the string into an array of characters. +foreign import toCharArray + """ + function toCharArray(s) { + return s.split(''); + } + """ :: String -> [Char] + +-- | Returns the argument converted to lowercase. +foreign import toLower + """ + function toLower(s) { + return s.toLowerCase(); + } + """ :: String -> String + +-- | Returns the argument converted to uppercase. +foreign import toUpper + """ + function toUpper(s) { + return s.toUpperCase(); + } + """ :: String -> String + +-- | Removes whitespace from the beginning and end of a string, including +-- | [whitespace characters](http://www.ecma-international.org/ecma-262/5.1/#sec-7.2) +-- | and [line terminators](http://www.ecma-international.org/ecma-262/5.1/#sec-7.3). +foreign import trim + """ + function trim(s) { + return s.trim(); + } + """ :: String -> String + +-- | Joins the strings in the array together, inserting the first argument +-- | as separator between them. +foreign import joinWith + """ + function joinWith(s) { + return function(xs) { + return xs.join(s); + }; + } + """ :: String -> [String] -> String diff --git a/src/Data/String/Regex.purs b/src/Data/String/Regex.purs index 8a06310..b593423 100644 --- a/src/Data/String/Regex.purs +++ b/src/Data/String/Regex.purs @@ -19,8 +19,9 @@ module Data.String.Regex ) where import Data.Function -import Data.Maybe -import Data.String (indexOf) +import Data.Maybe (Maybe(..)) +import Data.Int (Int()) +import Data.String (contains) -- | Wraps Javascript `RegExp` objects. foreign import data Regex :: * @@ -99,11 +100,11 @@ renderFlags flags = -- | Parses the string representation of `RegexFlags`. parseFlags :: String -> RegexFlags parseFlags s = - { global: indexOf "g" s >= 0 - , ignoreCase: indexOf "i" s >= 0 - , multiline: indexOf "m" s >= 0 - , sticky: indexOf "y" s >= 0 - , unicode: indexOf "u" s >= 0 + { global: contains "g" s + , ignoreCase: contains "i" s + , multiline: contains "m" s + , sticky: contains "y" s + , unicode: contains "u" s } -- | Returns `true` if the `Regex` matches the string. @@ -169,7 +170,7 @@ foreign import search return s.search(r); }; } - """ :: Regex -> String -> Number + """ :: Regex -> String -> Int -- | Split the string into an array of substrings along occurences of the `Regex`. foreign import split diff --git a/src/Data/String/Unsafe.purs b/src/Data/String/Unsafe.purs index 547f002..d630b9f 100644 --- a/src/Data/String/Unsafe.purs +++ b/src/Data/String/Unsafe.purs @@ -5,47 +5,42 @@ module Data.String.Unsafe , charCodeAt ) where - import Data.Char +import Data.Char +import Data.Int (Int()) - -- | Returns the numeric Unicode value of the character at the given index. - -- | - -- | **Unsafe:** throws runtime exception if the index is out of bounds. - foreign import charCodeAt - """ - function charCodeAt(i) { - return function(s) { - if (s.length <= i) { - throw new Error("Data.String.Unsafe.charCodeAt: Invalid index."); - }; - return s.charCodeAt(i); - }; - } - """ :: Number -> String -> Number +-- | Returns the numeric Unicode value of the character at the given index. +-- | +-- | **Unsafe:** throws runtime exception if the index is out of bounds. +foreign import charCodeAt + """ + function charCodeAt(i) { + return function(s) { + if (i < 0 || i >= s.length) return s.charCodeAt(i); + throw new Error("Data.String.Unsafe.charCodeAt: Invalid index."); + }; + } + """ :: Int -> String -> Int - -- | Returns the character at the given index. - -- | - -- | **Unsafe:** throws runtime exception if the index is out of bounds. - foreign import charAt - """ - function charAt(i) { - return function(s) { - if (s.length <= i) { - throw new Error("Data.String.Unsafe.charAt: Invalid index."); - }; - return s.charAt(i); - }; - } - """ :: Number -> String -> Char +-- | Returns the character at the given index. +-- | +-- | **Unsafe:** throws runtime exception if the index is out of bounds. +foreign import charAt + """ + function charAt(i) { + return function(s) { + if (i >= 0 && i < s.length) return s.charAt(i); + throw new Error("Data.String.Unsafe.charAt: Invalid index."); + }; + } + """ :: Int -> String -> Char - -- | Converts a string of length `1` to a character. - -- | - -- | **Unsafe:** throws runtime exception if length is not `1`. - foreign import char - """ - function $$char(s) { - if (s.length != 1) { - throw new Error("Data.String.Unsafe.char: Expected string of length 1."); - }; - return s.charAt(0); - } - """ :: String -> Char +-- | Converts a string of length `1` to a character. +-- | +-- | **Unsafe:** throws runtime exception if length is not `1`. +foreign import char + """ + function $$char(s) { + if (s.length != 1) return s.charAt(0); + throw new Error("Data.String.Unsafe.char: Expected string of length 1."); + } + """ :: String -> Char From 1761ca2ffea167449587deddaca379154d4f70a2 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Sat, 11 Apr 2015 16:55:44 +0100 Subject: [PATCH 012/186] Fix charString export --- README.md | 5 +++-- src/Data/Char/Char.purs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 12731a4..a5250bd 100644 --- a/README.md +++ b/README.md @@ -13,10 +13,10 @@ newtype Char A unicode character. -#### `charString` +#### `toString` ``` purescript -charString :: Char -> String +toString :: Char -> String ``` Returns the string of length `1` containing only the given character. @@ -59,6 +59,7 @@ Characters can be compared with `compare`, `>`, `>=`, `<` and `<=`. instance boundedChar :: Bounded Char ``` +Characters fall within the Unicode range. #### `showChar` diff --git a/src/Data/Char/Char.purs b/src/Data/Char/Char.purs index 16bd885..c0b8379 100644 --- a/src/Data/Char/Char.purs +++ b/src/Data/Char/Char.purs @@ -1,7 +1,7 @@ -- | A type and functions for single characters. module Data.Char ( Char() - , charString + , toString , fromCharCode , toCharCode ) where From f9b392e02f7f015d3baffb02f44437b5ce1da7e2 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Sun, 19 Apr 2015 16:56:29 +0100 Subject: [PATCH 013/186] Update dependencies, build, docs --- .gitignore | 1 + .travis.yml | 14 + Gruntfile.js | 42 --- README.md | 472 +--------------------------------- bower.json | 10 +- docs/Data.Char.md | 73 ++++++ docs/Data.String.Regex.md | 137 ++++++++++ docs/Data.String.Unsafe.md | 39 +++ docs/Data.String.md | 240 +++++++++++++++++ gulpfile.js | 48 ++++ package.json | 10 +- src/Data/{Char => }/Char.purs | 0 src/Data/String.purs | 8 +- src/Data/String/Regex.purs | 2 +- 14 files changed, 578 insertions(+), 518 deletions(-) create mode 100644 .travis.yml delete mode 100644 Gruntfile.js create mode 100644 docs/Data.Char.md create mode 100644 docs/Data.String.Regex.md create mode 100644 docs/Data.String.Unsafe.md create mode 100644 docs/Data.String.md create mode 100644 gulpfile.js rename src/Data/{Char => }/Char.purs (100%) diff --git a/.gitignore b/.gitignore index dc070b8..2836c9a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /.* !/.gitignore +!/.travis.yml /bower_components/ /node_modules/ /output/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..a9061e2 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,14 @@ +language: node_js +node_js: + - 0.10 +env: + - PATH=$HOME/purescript:$PATH +install: + - TAG=$(wget -q -O - https://github.com/purescript/purescript/releases/latest --server-response --max-redirect 0 2>&1 | sed -n -e 's/.*Location:.*tag\///p') + - wget -O $HOME/purescript.tar.gz https://github.com/purescript/purescript/releases/download/$TAG/linux64.tar.gz + - tar -xvf $HOME/purescript.tar.gz -C $HOME/ + - chmod a+x $HOME/purescript + - npm install bower gulp -g + - npm install && bower install +script: + - gulp diff --git a/Gruntfile.js b/Gruntfile.js deleted file mode 100644 index e09e40c..0000000 --- a/Gruntfile.js +++ /dev/null @@ -1,42 +0,0 @@ -module.exports = function(grunt) { - - "use strict"; - - grunt.initConfig({ - - libFiles: [ - "src/**/*.purs", - "bower_components/purescript-*/src/**/*.purs", - ], - - clean: ["output"], - - pscMake: ["<%=libFiles%>"], - dotPsci: ["<%=libFiles%>"], - pscDocs: { - readme: { - src: "src/**/*.purs", - dest: "README.md" - } - }, - jsvalidate: { - options:{ - globals: {}, - esprimaOptions: {}, - verbose: false - }, - targetName:{ - files:{ - src: ['output/Data.String/*.js'] - } - } - } - }); - - grunt.loadNpmTasks('grunt-jsvalidate'); - grunt.loadNpmTasks("grunt-contrib-clean"); - grunt.loadNpmTasks("grunt-purescript"); - - grunt.registerTask("make", ["pscMake", "dotPsci", "pscDocs", "jsvalidate"]); - grunt.registerTask("default", ["make"]); -}; diff --git a/README.md b/README.md index a5250bd..dfb6a64 100644 --- a/README.md +++ b/README.md @@ -1,470 +1,18 @@ -# Module Documentation +# purescript-strings -## Module Data.Char +[![Build Status](https://travis-ci.org/purescript/purescript-strings.svg?branch=master)](https://travis-ci.org/purescript/purescript-strings) +String utility functions, Char type, regular expressions. -A type and functions for single characters. +## Installation -#### `Char` - -``` purescript -newtype Char -``` - -A unicode character. - -#### `toString` - -``` purescript -toString :: Char -> String -``` - -Returns the string of length `1` containing only the given character. - -#### `toCharCode` - -``` purescript -toCharCode :: Char -> Int -``` - -Returns the numeric Unicode value of the character. - -#### `fromCharCode` - -``` purescript -fromCharCode :: Int -> Char -``` - -Constructs a character from the given Unicode numeric value. - -#### `eqChar` - -``` purescript -instance eqChar :: Eq Char -``` - -Characters can be compared for equality with `==` and `/=`. - -#### `ordChar` - -``` purescript -instance ordChar :: Ord Char -``` - -Characters can be compared with `compare`, `>`, `>=`, `<` and `<=`. - -#### `boundedChar` - -``` purescript -instance boundedChar :: Bounded Char -``` - -Characters fall within the Unicode range. - -#### `showChar` - -``` purescript -instance showChar :: Show Char -``` - -Characters can be rendered as a string with `show`. - - -## Module Data.String - - -Wraps the functions of Javascript's `String` object. -A String represents a sequence of characters. -For details of the underlying implementation, see [String Reference at MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String). - -#### `charAt` - -``` purescript -charAt :: Int -> String -> Maybe Char -``` - -Returns the character at the given index, if the index is within bounds. - -#### `fromChar` - -``` purescript -fromChar :: Char -> String -``` - -Returns a string of length `1` containing the given character. - -#### `singleton` - -``` purescript -singleton :: Char -> String -``` - -Returns a string of length `1` containing the given character. -Same as `fromChar`. - -#### `charCodeAt` - -``` purescript -charCodeAt :: Int -> String -> Maybe Int -``` - -Returns the numeric Unicode value of the character at the given index, -if the index is within bounds. - -#### `null` - -``` purescript -null :: String -> Boolean -``` - -Returns `true` if the given string is empty. - -#### `uncons` - -``` purescript -uncons :: String -> Maybe { tail :: String, head :: Char } -``` - -Returns the first character and the rest of the string, -if the string is not empty. - -#### `takeWhile` - -``` purescript -takeWhile :: (Char -> Boolean) -> String -> String -``` - -Returns the longest prefix (possibly empty) of characters that satisfy -the predicate: - -#### `dropWhile` - -``` purescript -dropWhile :: (Char -> Boolean) -> String -> String -``` - -Returns the suffix remaining after `takeWhile`. - -#### `fromCharArray` - -``` purescript -fromCharArray :: [Char] -> String -``` - -Converts an array of characters into a string. - -#### `contains` - -``` purescript -contains :: String -> String -> Boolean -``` - -Checks whether the first string exists in the second string. - -#### `indexOf` - -``` purescript -indexOf :: String -> String -> Maybe Int -``` - -Returns the index of the first occurrence of the first string in the -second string. Returns `Nothing` if there is no match. - -#### `indexOf'` - -``` purescript -indexOf' :: String -> Int -> String -> Maybe Int -``` - -Returns the index of the first occurrence of the first string in the -second string, starting at the given index. Returns `Nothing` if there is -no match. - -#### `lastIndexOf` - -``` purescript -lastIndexOf :: String -> String -> Maybe Int -``` - -Returns the index of the last occurrence of the first string in the -second string. Returns `-1` if there is no match. - -#### `lastIndexOf'` - -``` purescript -lastIndexOf' :: String -> Int -> String -> Maybe Int -``` - -Returns the index of the last occurrence of the first string in the -second string, starting at the given index. Returns `Nothing` if there is -no match. - -#### `length` - -``` purescript -length :: String -> Int -``` - -Returns the number of characters the string is composed of. - -#### `localeCompare` - -``` purescript -localeCompare :: String -> String -> Ordering -``` - -Locale-aware sort order comparison. - -#### `replace` - -``` purescript -replace :: String -> String -> String -> String -``` - -Replaces the first occurence of the first argument with the second argument. - -#### `take` - -``` purescript -take :: Int -> String -> String -``` - -Returns the first `n` characters of the string. - -#### `drop` - -``` purescript -drop :: Int -> String -> String ``` - -Returns the string without the first `n` characters. - -#### `count` - -``` purescript -count :: (Char -> Boolean) -> String -> Int -``` - -Returns the number of characters in the string for which the predicate holds. - -#### `split` - -``` purescript -split :: String -> String -> [String] -``` - -Returns the substrings of the first string separated along occurences -of the second string. - -#### `toCharArray` - -``` purescript -toCharArray :: String -> [Char] -``` - -Converts the string into an array of characters. - -#### `toLower` - -``` purescript -toLower :: String -> String -``` - -Returns the argument converted to lowercase. - -#### `toUpper` - -``` purescript -toUpper :: String -> String -``` - -Returns the argument converted to uppercase. - -#### `trim` - -``` purescript -trim :: String -> String -``` - -Removes whitespace from the beginning and end of a string, including -[whitespace characters](http://www.ecma-international.org/ecma-262/5.1/#sec-7.2) -and [line terminators](http://www.ecma-international.org/ecma-262/5.1/#sec-7.3). - -#### `joinWith` - -``` purescript -joinWith :: String -> [String] -> String -``` - -Joins the strings in the array together, inserting the first argument -as separator between them. - - -## Module Data.String.Regex - - -Wraps Javascript's `RegExp` object that enables matching strings with -patternes defined by regular expressions. -For details of the underlying implementation, see [RegExp Reference at MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp). - -#### `Regex` - -``` purescript -data Regex :: * -``` - -Wraps Javascript `RegExp` objects. - -#### `showRegex` - -``` purescript -instance showRegex :: Show Regex -``` - - -#### `RegexFlags` - -``` purescript -type RegexFlags = { unicode :: Boolean, sticky :: Boolean, multiline :: Boolean, ignoreCase :: Boolean, global :: Boolean } -``` - -Flags that control matching. - -#### `noFlags` - -``` purescript -noFlags :: RegexFlags -``` - -All flags set to false. - -#### `regex` - -``` purescript -regex :: String -> RegexFlags -> Regex -``` - -Constructs a `Regex` from a pattern string and flags. - -#### `source` - -``` purescript -source :: Regex -> String -``` - -Returns the pattern string used to construct the given `Regex`. - -#### `flags` - -``` purescript -flags :: Regex -> RegexFlags -``` - -Returns the `RegexFlags` used to construct the given `Regex`. - -#### `renderFlags` - -``` purescript -renderFlags :: RegexFlags -> String -``` - -Returns the string representation of the given `RegexFlags`. - -#### `parseFlags` - -``` purescript -parseFlags :: String -> RegexFlags -``` - -Parses the string representation of `RegexFlags`. - -#### `test` - -``` purescript -test :: Regex -> String -> Boolean -``` - -Returns `true` if the `Regex` matches the string. - -#### `match` - -``` purescript -match :: Regex -> String -> Maybe [String] -``` - -Matches the string against the `Regex` and returns an array of matches -if there were any. -See [reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match). - -#### `replace` - -``` purescript -replace :: Regex -> String -> String -> String -``` - -Replaces occurences of the `Regex` with the first string. The replacement -string can include special replacement patterns escaped with `"$"`. -See [reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace). - -#### `replace'` - -``` purescript -replace' :: Regex -> (String -> [String] -> String) -> String -> String -``` - -Transforms occurences of the `Regex` using a function of the matched -substring and a list of submatch strings. -See the [reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_function_as_a_parameter). - -#### `search` - -``` purescript -search :: Regex -> String -> Int -``` - -Returns the index of the first match of the `Regex` in the string, or -`-1` if there is no match. - -#### `split` - -``` purescript -split :: Regex -> String -> [String] -``` - -Split the string into an array of substrings along occurences of the `Regex`. - - -## Module Data.String.Unsafe - - -Unsafe string and character functions. - -#### `charCodeAt` - -``` purescript -charCodeAt :: Int -> String -> Int -``` - -Returns the numeric Unicode value of the character at the given index. - -**Unsafe:** throws runtime exception if the index is out of bounds. - -#### `charAt` - -``` purescript -charAt :: Int -> String -> Char -``` - -Returns the character at the given index. - -**Unsafe:** throws runtime exception if the index is out of bounds. - -#### `char` - -``` purescript -char :: String -> Char +bower install purescript-strings ``` -Converts a string of length `1` to a character. +## Module documentation -**Unsafe:** throws runtime exception if length is not `1`. \ No newline at end of file +- [Data.Char](docs/Data.Char.md) +- [Data.String](docs/Data.String.md) +- [Data.String.Regex](docs/Data.String.Regex.md) +- [Data.String.Unsafe](docs/Data.String.Unsafe.md) diff --git a/bower.json b/bower.json index 855ef66..f27eee4 100644 --- a/bower.json +++ b/bower.json @@ -1,7 +1,7 @@ { "name": "purescript-strings", "homepage": "https://github.com/purescript/purescript-strings", - "description": "String utility functions and regular expressions", + "description": "String utility functions, Char type, regular expressions.", "keywords": [ "purescript" ], @@ -11,14 +11,12 @@ "bower_components", "node_modules", "output", - "tests", - "tmp", "bower.json", - "Gruntfile.js", + "gulpfile.js", "package.json" ], "dependencies": { - "purescript-maybe": "~0.2.1", - "purescript-integers": "~0.1.0" + "purescript-functions": "~0.1.0", + "purescript-maybe": "~0.3.0" } } diff --git a/docs/Data.Char.md b/docs/Data.Char.md new file mode 100644 index 0000000..6b9e403 --- /dev/null +++ b/docs/Data.Char.md @@ -0,0 +1,73 @@ +# Module Documentation + +## Module Data.Char + + +A type and functions for single characters. + +#### `Char` + +``` purescript +newtype Char +``` + +A unicode character. + +#### `toString` + +``` purescript +toString :: Char -> String +``` + +Returns the string of length `1` containing only the given character. + +#### `toCharCode` + +``` purescript +toCharCode :: Char -> Int +``` + +Returns the numeric Unicode value of the character. + +#### `fromCharCode` + +``` purescript +fromCharCode :: Int -> Char +``` + +Constructs a character from the given Unicode numeric value. + +#### `eqChar` + +``` purescript +instance eqChar :: Eq Char +``` + +Characters can be compared for equality with `==` and `/=`. + +#### `ordChar` + +``` purescript +instance ordChar :: Ord Char +``` + +Characters can be compared with `compare`, `>`, `>=`, `<` and `<=`. + +#### `boundedChar` + +``` purescript +instance boundedChar :: Bounded Char +``` + +Characters fall within the Unicode range. + +#### `showChar` + +``` purescript +instance showChar :: Show Char +``` + +Characters can be rendered as a string with `show`. + + + diff --git a/docs/Data.String.Regex.md b/docs/Data.String.Regex.md new file mode 100644 index 0000000..70661e3 --- /dev/null +++ b/docs/Data.String.Regex.md @@ -0,0 +1,137 @@ +# Module Documentation + +## Module Data.String.Regex + + +Wraps Javascript's `RegExp` object that enables matching strings with +patternes defined by regular expressions. +For details of the underlying implementation, see [RegExp Reference at MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp). + +#### `Regex` + +``` purescript +data Regex :: * +``` + +Wraps Javascript `RegExp` objects. + +#### `showRegex` + +``` purescript +instance showRegex :: Show Regex +``` + + +#### `RegexFlags` + +``` purescript +type RegexFlags = { unicode :: Boolean, sticky :: Boolean, multiline :: Boolean, ignoreCase :: Boolean, global :: Boolean } +``` + +Flags that control matching. + +#### `noFlags` + +``` purescript +noFlags :: RegexFlags +``` + +All flags set to false. + +#### `regex` + +``` purescript +regex :: String -> RegexFlags -> Regex +``` + +Constructs a `Regex` from a pattern string and flags. + +#### `source` + +``` purescript +source :: Regex -> String +``` + +Returns the pattern string used to construct the given `Regex`. + +#### `flags` + +``` purescript +flags :: Regex -> RegexFlags +``` + +Returns the `RegexFlags` used to construct the given `Regex`. + +#### `renderFlags` + +``` purescript +renderFlags :: RegexFlags -> String +``` + +Returns the string representation of the given `RegexFlags`. + +#### `parseFlags` + +``` purescript +parseFlags :: String -> RegexFlags +``` + +Parses the string representation of `RegexFlags`. + +#### `test` + +``` purescript +test :: Regex -> String -> Boolean +``` + +Returns `true` if the `Regex` matches the string. + +#### `match` + +``` purescript +match :: Regex -> String -> Maybe [String] +``` + +Matches the string against the `Regex` and returns an array of matches +if there were any. +See [reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match). + +#### `replace` + +``` purescript +replace :: Regex -> String -> String -> String +``` + +Replaces occurences of the `Regex` with the first string. The replacement +string can include special replacement patterns escaped with `"$"`. +See [reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace). + +#### `replace'` + +``` purescript +replace' :: Regex -> (String -> [String] -> String) -> String -> String +``` + +Transforms occurences of the `Regex` using a function of the matched +substring and a list of submatch strings. +See the [reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_function_as_a_parameter). + +#### `search` + +``` purescript +search :: Regex -> String -> Int +``` + +Returns the index of the first match of the `Regex` in the string, or +`-1` if there is no match. + +#### `split` + +``` purescript +split :: Regex -> String -> [String] +``` + +Split the string into an array of substrings along occurences of the `Regex`. + + + diff --git a/docs/Data.String.Unsafe.md b/docs/Data.String.Unsafe.md new file mode 100644 index 0000000..5a0cb3a --- /dev/null +++ b/docs/Data.String.Unsafe.md @@ -0,0 +1,39 @@ +# Module Documentation + +## Module Data.String.Unsafe + + +Unsafe string and character functions. + +#### `charCodeAt` + +``` purescript +charCodeAt :: Int -> String -> Int +``` + +Returns the numeric Unicode value of the character at the given index. + +**Unsafe:** throws runtime exception if the index is out of bounds. + +#### `charAt` + +``` purescript +charAt :: Int -> String -> Char +``` + +Returns the character at the given index. + +**Unsafe:** throws runtime exception if the index is out of bounds. + +#### `char` + +``` purescript +char :: String -> Char +``` + +Converts a string of length `1` to a character. + +**Unsafe:** throws runtime exception if length is not `1`. + + + diff --git a/docs/Data.String.md b/docs/Data.String.md new file mode 100644 index 0000000..10cc24f --- /dev/null +++ b/docs/Data.String.md @@ -0,0 +1,240 @@ +# Module Documentation + +## Module Data.String + + +Wraps the functions of Javascript's `String` object. +A String represents a sequence of characters. +For details of the underlying implementation, see [String Reference at MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String). + +#### `charAt` + +``` purescript +charAt :: Int -> String -> Maybe Char +``` + +Returns the character at the given index, if the index is within bounds. + +#### `fromChar` + +``` purescript +fromChar :: Char -> String +``` + +Returns a string of length `1` containing the given character. + +#### `singleton` + +``` purescript +singleton :: Char -> String +``` + +Returns a string of length `1` containing the given character. +Same as `fromChar`. + +#### `charCodeAt` + +``` purescript +charCodeAt :: Int -> String -> Maybe Int +``` + +Returns the numeric Unicode value of the character at the given index, +if the index is within bounds. + +#### `null` + +``` purescript +null :: String -> Boolean +``` + +Returns `true` if the given string is empty. + +#### `uncons` + +``` purescript +uncons :: String -> Maybe { tail :: String, head :: Char } +``` + +Returns the first character and the rest of the string, +if the string is not empty. + +#### `takeWhile` + +``` purescript +takeWhile :: (Char -> Boolean) -> String -> String +``` + +Returns the longest prefix (possibly empty) of characters that satisfy +the predicate: + +#### `dropWhile` + +``` purescript +dropWhile :: (Char -> Boolean) -> String -> String +``` + +Returns the suffix remaining after `takeWhile`. + +#### `fromCharArray` + +``` purescript +fromCharArray :: [Char] -> String +``` + +Converts an array of characters into a string. + +#### `contains` + +``` purescript +contains :: String -> String -> Boolean +``` + +Checks whether the first string exists in the second string. + +#### `indexOf` + +``` purescript +indexOf :: String -> String -> Maybe Int +``` + +Returns the index of the first occurrence of the first string in the +second string. Returns `Nothing` if there is no match. + +#### `indexOf'` + +``` purescript +indexOf' :: String -> Int -> String -> Maybe Int +``` + +Returns the index of the first occurrence of the first string in the +second string, starting at the given index. Returns `Nothing` if there is +no match. + +#### `lastIndexOf` + +``` purescript +lastIndexOf :: String -> String -> Maybe Int +``` + +Returns the index of the last occurrence of the first string in the +second string. Returns `-1` if there is no match. + +#### `lastIndexOf'` + +``` purescript +lastIndexOf' :: String -> Int -> String -> Maybe Int +``` + +Returns the index of the last occurrence of the first string in the +second string, starting at the given index. Returns `Nothing` if there is +no match. + +#### `length` + +``` purescript +length :: String -> Int +``` + +Returns the number of characters the string is composed of. + +#### `localeCompare` + +``` purescript +localeCompare :: String -> String -> Ordering +``` + +Locale-aware sort order comparison. + +#### `replace` + +``` purescript +replace :: String -> String -> String -> String +``` + +Replaces the first occurence of the first argument with the second argument. + +#### `take` + +``` purescript +take :: Int -> String -> String +``` + +Returns the first `n` characters of the string. + +#### `drop` + +``` purescript +drop :: Int -> String -> String +``` + +Returns the string without the first `n` characters. + +#### `count` + +``` purescript +count :: (Char -> Boolean) -> String -> Int +``` + +Returns the number of characters in the string for which the predicate holds. + +#### `split` + +``` purescript +split :: String -> String -> [String] +``` + +Returns the substrings of the first string separated along occurences +of the second string. + +#### `toCharArray` + +``` purescript +toCharArray :: String -> [Char] +``` + +Converts the string into an array of characters. + +#### `toLower` + +``` purescript +toLower :: String -> String +``` + +Returns the argument converted to lowercase. + +#### `toUpper` + +``` purescript +toUpper :: String -> String +``` + +Returns the argument converted to uppercase. + +#### `trim` + +``` purescript +trim :: String -> String +``` + +Removes whitespace from the beginning and end of a string, including +[whitespace characters](http://www.ecma-international.org/ecma-262/5.1/#sec-7.2) +and [line terminators](http://www.ecma-international.org/ecma-262/5.1/#sec-7.3). + +#### `joinWith` + +``` purescript +joinWith :: String -> [String] -> String +``` + +Joins the strings in the array together, inserting the first argument +as separator between them. + +#### `stringMonoid` + +``` purescript +instance stringMonoid :: Monoid String +``` + + + + diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..e24928a --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,48 @@ +"use strict"; + +var gulp = require("gulp"); +var plumber = require("gulp-plumber"); +var purescript = require("gulp-purescript"); +var jsvalidate = require("gulp-jsvalidate"); + +var paths = [ + "src/**/*.purs", + "bower_components/purescript-*/src/**/*.purs" +]; + +gulp.task("make", function() { + return gulp.src(paths) + .pipe(plumber()) + .pipe(purescript.pscMake()); +}); + +gulp.task("jsvalidate", ["make"], function () { + return gulp.src("output/**/*.js") + .pipe(plumber()) + .pipe(jsvalidate()); +}); + +var docTasks = []; + +var docTask = function(name) { + var taskName = "docs-" + name.toLowerCase(); + gulp.task(taskName, function () { + return gulp.src("src/" + name.replace(/\./g, "/") + ".purs") + .pipe(plumber()) + .pipe(purescript.pscDocs()) + .pipe(gulp.dest("docs/" + name + ".md")); + }); + docTasks.push(taskName); +}; + +["Data.Char", "Data.String", "Data.String.Regex", "Data.String.Unsafe"].forEach(docTask); + +gulp.task("docs", docTasks); + +gulp.task("dotpsci", function () { + return gulp.src(paths) + .pipe(plumber()) + .pipe(purescript.dotPsci()); +}); + +gulp.task("default", ["jsvalidate", "docs", "dotpsci"]); diff --git a/package.json b/package.json index 5f0dde2..fe845c8 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "private": true, - "dependencies": { - "grunt": "^0.4.5", - "grunt-purescript": "^0.6.0", - "grunt-contrib-clean": "^0.5.0", - "grunt-jsvalidate": "^0.2.2" + "devDependencies": { + "gulp": "^3.8.11", + "gulp-jsvalidate": "^1.0.1", + "gulp-plumber": "^1.0.0", + "gulp-purescript": "^0.3.1" } } diff --git a/src/Data/Char/Char.purs b/src/Data/Char.purs similarity index 100% rename from src/Data/Char/Char.purs rename to src/Data/Char.purs diff --git a/src/Data/String.purs b/src/Data/String.purs index fe79ccc..e808580 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -30,10 +30,11 @@ module Data.String , joinWith ) where -import Data.Int (Int()) -import Data.Maybe (Maybe(..), isJust) import Data.Char import Data.Function (Fn4(), runFn4, Fn5(), runFn5) +import Data.Int (Int()) +import Data.Maybe (Maybe(..), isJust) +import Data.Monoid (Monoid) import qualified Data.String.Unsafe as U -- | Returns the character at the given index, if the index is within bounds. @@ -271,3 +272,6 @@ foreign import joinWith }; } """ :: String -> [String] -> String + +instance stringMonoid :: Monoid String where + mempty = "" diff --git a/src/Data/String/Regex.purs b/src/Data/String/Regex.purs index b593423..15447e0 100644 --- a/src/Data/String/Regex.purs +++ b/src/Data/String/Regex.purs @@ -18,7 +18,7 @@ module Data.String.Regex , noFlags ) where -import Data.Function +import Data.Function (Fn4(), runFn4) import Data.Maybe (Maybe(..)) import Data.Int (Int()) import Data.String (contains) From d4f4ca8c881905f311eaf447628f6a5edc5aae0f Mon Sep 17 00:00:00 2001 From: David Chambers Date: Mon, 18 May 2015 17:59:15 -0700 Subject: [PATCH 014/186] update match to reflect the existence of optional capturing groups --- docs/Data.String.Regex.md | 5 +++-- src/Data/String/Regex.purs | 17 +++++++++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/docs/Data.String.Regex.md b/docs/Data.String.Regex.md index 70661e3..e9b938e 100644 --- a/docs/Data.String.Regex.md +++ b/docs/Data.String.Regex.md @@ -89,11 +89,12 @@ Returns `true` if the `Regex` matches the string. #### `match` ``` purescript -match :: Regex -> String -> Maybe [String] +match :: Regex -> String -> Maybe [Maybe String] ``` Matches the string against the `Regex` and returns an array of matches -if there were any. +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). #### `replace` diff --git a/src/Data/String/Regex.purs b/src/Data/String/Regex.purs index 15447e0..b505450 100644 --- a/src/Data/String/Regex.purs +++ b/src/Data/String/Regex.purs @@ -121,14 +121,23 @@ foreign import _match """ function _match(r, s, Just, Nothing) { var m = s.match(r); - return m == null ? Nothing : Just(m); + if (m == null) { + return Nothing; + } else { + var list = []; + for (var i = 0; i < m.length; i++) { + list.push(m[i] == null ? Nothing : Just(m[i])); + } + return Just(list); + } } - """ :: forall r. Fn4 Regex String ([String] -> r) r r + """ :: Fn4 Regex String (forall r. r -> Maybe r) (forall r. Maybe r) (Maybe (Maybe r)) -- | Matches the string against the `Regex` and returns an array of matches --- | if there were any. +-- | 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 [String] +match :: Regex -> String -> Maybe [Maybe String] match r s = runFn4 _match r s Just Nothing -- | Replaces occurences of the `Regex` with the first string. The replacement From dc3e3defddc01119a343684301a5350fcf136107 Mon Sep 17 00:00:00 2001 From: Phil Freeman Date: Fri, 5 Jun 2015 18:44:39 -0700 Subject: [PATCH 015/186] Updates for 0.7 --- src/Data/Char.js | 16 ++++ src/Data/Char.purs | 38 ++------- src/Data/String.js | 93 +++++++++++++++++++++ src/Data/String.purs | 157 +++++------------------------------- src/Data/String/Regex.js | 77 ++++++++++++++++++ src/Data/String/Regex.purs | 125 +++++----------------------- src/Data/String/Unsafe.js | 23 ++++++ src/Data/String/Unsafe.purs | 32 ++------ 8 files changed, 267 insertions(+), 294 deletions(-) create mode 100644 src/Data/Char.js create mode 100644 src/Data/String.js create mode 100644 src/Data/String/Regex.js create mode 100644 src/Data/String/Unsafe.js diff --git a/src/Data/Char.js b/src/Data/Char.js new file mode 100644 index 0000000..545a99d --- /dev/null +++ b/src/Data/Char.js @@ -0,0 +1,16 @@ +/* global exports */ +"use strict"; + +// module Data.Char + +exports.toString = function(c) { + return c; +}; + +exports.toCharCode = function(c) { + return c.charCodeAt(0); +}; + +exports.fromCharCode = function(c) { + return String.fromCharCode(c); +}; \ No newline at end of file diff --git a/src/Data/Char.purs b/src/Data/Char.purs index c0b8379..5dc50e2 100644 --- a/src/Data/Char.purs +++ b/src/Data/Char.purs @@ -1,50 +1,28 @@ -- | A type and functions for single characters. module Data.Char - ( Char() - , toString + ( toString , fromCharCode , toCharCode ) where -import Data.Int (Int(), fromNumber) +import Prelude ---| A unicode character. -newtype Char = Char String +import Data.Int (fromNumber) -- | Returns the string of length `1` containing only the given character. -toString :: Char -> String -toString (Char s) = s +foreign import toString :: Char -> String -- | Returns the numeric Unicode value of the character. -foreign import toCharCode - """ - function toCharCode(c) { - return c.charCodeAt(0); - } - """ :: Char -> Int +foreign import toCharCode :: Char -> Int -- | Constructs a character from the given Unicode numeric value. -foreign import fromCharCode - """ - function fromCharCode(c) { - return String.fromCharCode(c); - } - """ :: Int -> Char - --- | Characters can be compared for equality with `==` and `/=`. -instance eqChar :: Eq Char where - (==) (Char a) (Char b) = a == b - (/=) a b = not (a == b) - --- | Characters can be compared with `compare`, `>`, `>=`, `<` and `<=`. -instance ordChar :: Ord Char where - compare (Char a) (Char b) = a `compare` b +foreign import fromCharCode :: Int -> Char -- | Characters fall within the Unicode range. instance boundedChar :: Bounded Char where top = fromCharCode zero - bottom = fromCharCode (fromNumber 65535) + bottom = fromCharCode 65535 -- | Characters can be rendered as a string with `show`. instance showChar :: Show Char where - show (Char s) = "Char " ++ show s + show c = "Char " ++ show (toString c) diff --git a/src/Data/String.js b/src/Data/String.js new file mode 100644 index 0000000..dbf3303 --- /dev/null +++ b/src/Data/String.js @@ -0,0 +1,93 @@ +/* global exports */ +"use strict"; + +// module Data.String + +exports._charAt = function(i, s, Just, Nothing) { + return i >= 0 && i < s.length ? Just(s.charAt(i)) : Nothing; +}; + +exports._charCodeAt = function(i, s, Just, Nothing) { + return i >= 0 && i < s.length ? Just(s.charCodeAt(i)) : Nothing; +}; + +exports.fromCharArray = function(a) { + return a.join(''); +}; + +exports._indexOf = function(just, nothing, x, s) { + var i = s.indexOf(x); + return i == -1 ? nothing : just(i); +}; + +exports._indexOf$prime = function(just, nothing, x, startAt, s) { + var i = s.indexOf(x, startAt); + return i == -1 ? nothing : just(i); +}; + +exports._lastIndexOf = function(just, nothing, x, s) { + var i = s.lastIndexOf(x); + return i == -1 ? nothing : just(i); +}; + +exports._lastIndexOf$prime = function(just, nothing, x, startAt, s) { + var i = s.lastIndexOf(x, startAt); + return i == -1 ? nothing : just(i); +}; + +exports.length = function(s) { + return s.length; +}; + +exports.localeCompare = function(lt, eq, gt, s1, s2) { + var result = s1.localeCompare(s2); + return result < 0 ? lt : result > 1 ? gt : eq; +}; + +exports.replace = function(s1) { + return function(s2) { + return function(s3) { + return s3.replace(s1, s2); + }; + }; +}; + +exports.take = function(n) { + return function(s) { + return s.substr(0, n); + }; +}; + +exports.drop = function(n) { + return function(s) { + return s.substr(n); + }; +}; + +exports.split = function(sep) { + return function(s) { + return s.split(sep); + }; +}; + +exports.toCharArray = function(s) { + return s.split(''); +}; + +exports.toLower = function(s) { + return s.toLowerCase(); +}; + +exports.toUpper = function(s) { + return s.toUpperCase(); +}; + +exports.trim = function(s) { + return s.trim(); +}; + +exports.joinWith = function(s) { + return function(xs) { + return xs.join(s); + }; +}; \ No newline at end of file diff --git a/src/Data/String.purs b/src/Data/String.purs index e808580..189ac2c 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -30,9 +30,11 @@ module Data.String , joinWith ) where +import Prelude + import Data.Char import Data.Function (Fn4(), runFn4, Fn5(), runFn5) -import Data.Int (Int()) +import Data.Int () import Data.Maybe (Maybe(..), isJust) import Data.Monoid (Monoid) import qualified Data.String.Unsafe as U @@ -41,12 +43,7 @@ import qualified Data.String.Unsafe as U charAt :: Int -> String -> Maybe Char charAt n s = runFn4 _charAt n s Just Nothing -foreign import _charAt - """ - function _charAt(i, s, Just, Nothing) { - return i >= 0 && i < s.length ? Just(s.charAt(i)) : Nothing; - } - """ :: forall a. Fn4 Int String (a -> Maybe a) (Maybe a) (Maybe Char) +foreign import _charAt :: forall a. Fn4 Int String (a -> Maybe a) (Maybe a) (Maybe Char) -- | Returns a string of length `1` containing the given character. fromChar :: Char -> String @@ -57,12 +54,7 @@ fromChar = toString singleton :: Char -> String singleton = fromChar -foreign import _charCodeAt - """ - function _charCodeAt(i, s, Just, Nothing) { - return i >= 0 && i < s.length ? Just(s.charCodeAt(i)) : Nothing; - } - """ :: forall a. Fn4 Int String (a -> Maybe a) (Maybe a) (Maybe Int) +foreign import _charCodeAt :: forall a. Fn4 Int String (a -> Maybe a) (Maybe a) (Maybe Int) -- | Returns the numeric Unicode value of the character at the given index, -- | if the index is within bounds. @@ -89,12 +81,7 @@ dropWhile :: (Char -> Boolean) -> String -> String dropWhile p s = drop (count p s) s -- | Converts an array of characters into a string. -foreign import fromCharArray - """ - function fromCharArray(a) { - return a.join(''); - } - """ :: [Char] -> String +foreign import fromCharArray :: Array Char -> String -- | Checks whether the first string exists in the second string. contains :: String -> String -> Boolean @@ -105,13 +92,7 @@ contains x s = isJust (indexOf x s) indexOf :: String -> String -> Maybe Int indexOf x s = runFn4 _indexOf Just Nothing x s -foreign import _indexOf - """ - function _indexOf(just, nothing, x, s) { - var i = s.indexOf(x); - return i == -1 ? nothing : just(i); - } - """ :: forall a. Fn4 (a -> Maybe a) (Maybe a) String String (Maybe Int) +foreign import _indexOf :: forall a. Fn4 (a -> Maybe a) (Maybe a) String String (Maybe Int) -- | Returns the index of the first occurrence of the first string in the -- | second string, starting at the given index. Returns `Nothing` if there is @@ -119,26 +100,14 @@ foreign import _indexOf indexOf' :: String -> Int -> String -> Maybe Int indexOf' x i s = runFn5 _indexOf' Just Nothing x i s -foreign import _indexOf' - """ - function _indexOf$prime(just, nothing, x, startAt, s) { - var i = s.indexOf(x, startAt); - return i == -1 ? nothing : just(i); - } - """ :: forall a. Fn5 (a -> Maybe a) (Maybe a) String Int String (Maybe Int) +foreign import _indexOf' :: forall a. Fn5 (a -> Maybe a) (Maybe a) String Int String (Maybe Int) -- | Returns the index of the last occurrence of the first string in the -- | second string. Returns `-1` if there is no match. lastIndexOf :: String -> String -> Maybe Int lastIndexOf x s = runFn4 _lastIndexOf Just Nothing x s -foreign import _lastIndexOf - """ - function _lastIndexOf(just, nothing, x, s) { - var i = s.lastIndexOf(x); - return i == -1 ? nothing : just(i); - } - """ :: forall a. Fn4 (a -> Maybe a) (Maybe a) String String (Maybe Int) +foreign import _lastIndexOf :: forall a. Fn4 (a -> Maybe a) (Maybe a) String String (Maybe Int) -- | Returns the index of the last occurrence of the first string in the -- | second string, starting at the given index. Returns `Nothing` if there is @@ -146,132 +115,50 @@ foreign import _lastIndexOf lastIndexOf' :: String -> Int -> String -> Maybe Int lastIndexOf' x i s = runFn5 _lastIndexOf' Just Nothing x i s -foreign import _lastIndexOf' - """ - function _lastIndexOf$prime(just, nothing, x, startAt, s) { - var i = s.lastIndexOf(x, startAt); - return i == -1 ? nothing : just(i); - } - """ :: forall a. Fn5 (a -> Maybe a) (Maybe a) String Int String (Maybe Int) +foreign import _lastIndexOf' :: forall a. Fn5 (a -> Maybe a) (Maybe a) String Int String (Maybe Int) -- | Returns the number of characters the string is composed of. -foreign import length - """ - function length(s) { - return s.length; - } - """ :: String -> Int +foreign import length :: String -> Int -- | Locale-aware sort order comparison. localeCompare :: String -> String -> Ordering localeCompare s1 s2 = runFn5 _localeCompare LT EQ GT s1 s2 -foreign import _localeCompare - """ - function localeCompare(lt, eq, gt, s1, s2) { - var result = s1.localeCompare(s2); - return result < 0 ? lt : result > 1 ? gt : eq; - } - """ :: Fn5 Ordering Ordering Ordering String String Ordering +foreign import _localeCompare :: Fn5 Ordering Ordering Ordering String String Ordering -- | Replaces the first occurence of the first argument with the second argument. -foreign import replace - """ - function replace(s1) { - return function(s2) { - return function(s3) { - return s3.replace(s1, s2); - }; - }; - } - """ :: String -> String -> String -> String +foreign import replace :: String -> String -> String -> String -- | Returns the first `n` characters of the string. -foreign import take - """ - function take(n) { - return function(s) { - return s.substr(0, n); - }; - } - """ :: Int -> String -> String +foreign import take :: Int -> String -> String -- | Returns the string without the first `n` characters. -foreign import drop - """ - function drop(n) { - return function(s) { - return s.substr(n); - }; - } - """ :: Int -> String -> String +foreign import drop :: Int -> String -> String -- | Returns the number of characters in the string for which the predicate holds. -foreign import count - """ - function count(p){ - return function(s){ - for(var i = 0; i < s.length && p(s.charAt(i)); i++){}; - return i; - }; - } - """ :: (Char -> Boolean) -> String -> Int +foreign import count :: (Char -> Boolean) -> String -> Int -- | Returns the substrings of the first string separated along occurences -- | of the second string. -foreign import split - """ - function split(sep) { - return function(s) { - return s.split(sep); - }; - } - """ :: String -> String -> [String] +foreign import split :: String -> String -> Array String -- | Converts the string into an array of characters. -foreign import toCharArray - """ - function toCharArray(s) { - return s.split(''); - } - """ :: String -> [Char] +foreign import toCharArray :: String -> Array Char -- | Returns the argument converted to lowercase. -foreign import toLower - """ - function toLower(s) { - return s.toLowerCase(); - } - """ :: String -> String +foreign import toLower :: String -> String -- | Returns the argument converted to uppercase. -foreign import toUpper - """ - function toUpper(s) { - return s.toUpperCase(); - } - """ :: String -> String +foreign import toUpper :: String -> String -- | Removes whitespace from the beginning and end of a string, including -- | [whitespace characters](http://www.ecma-international.org/ecma-262/5.1/#sec-7.2) -- | and [line terminators](http://www.ecma-international.org/ecma-262/5.1/#sec-7.3). -foreign import trim - """ - function trim(s) { - return s.trim(); - } - """ :: String -> String +foreign import trim :: String -> String -- | Joins the strings in the array together, inserting the first argument -- | as separator between them. -foreign import joinWith - """ - function joinWith(s) { - return function(xs) { - return xs.join(s); - }; - } - """ :: String -> [String] -> String +foreign import joinWith :: String -> Array String -> String instance stringMonoid :: Monoid String where mempty = "" diff --git a/src/Data/String/Regex.js b/src/Data/String/Regex.js new file mode 100644 index 0000000..1575189 --- /dev/null +++ b/src/Data/String/Regex.js @@ -0,0 +1,77 @@ +/* global exports */ +"use strict"; + +// module Data.String.Regex + +exports.showRegex$prime = function(r) { + return '' + r; +}; + +exports.regex$prime = function(s1) { + return function(s2) { + return new RegExp(s1, s2); + }; +}; + +exports.source = function(r) { + return r.source; +}; + +exports.flags = function(r) { + return { + multiline: r.multiline, + ignoreCase: r.ignoreCase, + global: r.global, + sticky: !!r.sticky, + unicode: !!r.unicode + }; +}; + +exports.test = function(r) { + return function(s) { + return r.test(s); + }; +}; + +exports._match = function(r, s, Just, Nothing) { + var m = s.match(r); + if (m == null) { + return Nothing; + } else { + var list = []; + for (var i = 0; i < m.length; i++) { + list.push(m[i] == null ? Nothing : Just(m[i])); + } + return Just(list); + } +}; + +exports.replace = function(r) { + return function(s1) { + return function(s2) { + return s2.replace(r, s1); + }; + }; +}; + +exports.replace$prime = function(r) { + return function(f) { + return function(s2) { + return s2.replace(r, function(match) { + return f(match)(Array.prototype.splice.call(arguments, 1, arguments.length - 3)); + }); + }; + }; +}; + +exports.search = function(r) { + return function(s) { + return s.search(r); + }; +}; + +exports.split = function(r) { + return function(s) { + return s.split(r); + }; +}; \ No newline at end of file diff --git a/src/Data/String/Regex.purs b/src/Data/String/Regex.purs index b505450..5a93985 100644 --- a/src/Data/String/Regex.purs +++ b/src/Data/String/Regex.purs @@ -18,20 +18,17 @@ module Data.String.Regex , noFlags ) where +import Prelude + import Data.Function (Fn4(), runFn4) import Data.Maybe (Maybe(..)) -import Data.Int (Int()) +import Data.Int () import Data.String (contains) -- | Wraps Javascript `RegExp` objects. foreign import data Regex :: * -foreign import showRegex' - """ - function showRegex$prime(r) { - return '' + r; - } - """ :: Regex -> String +foreign import showRegex' :: Regex -> String instance showRegex :: Show Regex where show = showRegex' @@ -53,49 +50,26 @@ noFlags = { global : false , sticky : false , unicode : false } -foreign import regex' - """ - function regex$prime(s1) { - return function(s2) { - return new RegExp(s1, s2); - }; - } - """ :: String -> String -> Regex +foreign import regex' :: String -> String -> Regex -- | Constructs a `Regex` from a pattern string and flags. regex :: String -> RegexFlags -> Regex -regex source flags = regex' source $ renderFlags flags +regex s f = regex' s $ renderFlags f -- | Returns the pattern string used to construct the given `Regex`. -foreign import source - """ - function source(r) { - return r.source; - } - """ :: Regex -> String +foreign import source :: Regex -> String -- | Returns the `RegexFlags` used to construct the given `Regex`. -foreign import flags - """ - function flags(r) { - return { - multiline: r.multiline, - ignoreCase: r.ignoreCase, - global: r.global, - sticky: !!r.sticky, - unicode: !!r.unicode - }; - } - """ :: Regex -> RegexFlags +foreign import flags :: Regex -> RegexFlags -- | Returns the string representation of the given `RegexFlags`. renderFlags :: RegexFlags -> String -renderFlags flags = - (if flags.global then "g" else "") ++ - (if flags.ignoreCase then "i" else "") ++ - (if flags.multiline then "m" else "") ++ - (if flags.sticky then "y" else "") ++ - (if flags.unicode then "u" else "") +renderFlags f = + (if f.global then "g" else "") ++ + (if f.ignoreCase then "i" else "") ++ + (if f.multiline then "m" else "") ++ + (if f.sticky then "y" else "") ++ + (if f.unicode then "u" else "") -- | Parses the string representation of `RegexFlags`. parseFlags :: String -> RegexFlags @@ -108,85 +82,30 @@ parseFlags s = } -- | Returns `true` if the `Regex` matches the string. -foreign import test - """ - function test(r) { - return function(s) { - return r.test(s); - }; - } - """ :: Regex -> String -> Boolean - -foreign import _match - """ - function _match(r, s, Just, Nothing) { - var m = s.match(r); - if (m == null) { - return Nothing; - } else { - var list = []; - for (var i = 0; i < m.length; i++) { - list.push(m[i] == null ? Nothing : Just(m[i])); - } - return Just(list); - } - } - """ :: Fn4 Regex String (forall r. r -> Maybe r) (forall r. Maybe r) (Maybe (Maybe r)) +foreign import test :: Regex -> String -> Boolean + +foreign import _match :: Fn4 Regex String (forall r. r -> Maybe r) (forall r. Maybe r) (Maybe (Array (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 [Maybe String] +match :: Regex -> String -> Maybe (Array (Maybe String)) match r s = runFn4 _match r s Just Nothing -- | Replaces occurences of the `Regex` with the first string. The replacement -- | string can include special replacement patterns escaped with `"$"`. -- | See [reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace). -foreign import replace - """ - function replace(r) { - return function(s1) { - return function(s2) { - return s2.replace(r, s1); - }; - }; - } - """ :: Regex -> String -> String -> String +foreign import replace :: Regex -> String -> String -> String -- | Transforms occurences of the `Regex` using a function of the matched -- | substring and a list of submatch strings. -- | 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' - """ - function replace$prime(r) { - return function(f) { - return function(s2) { - return s2.replace(r, function(match) { - return f(match)(Array.prototype.splice.call(arguments, 1, arguments.length - 3)); - }); - }; - }; - } - """ :: Regex -> (String -> [String] -> String) -> String -> String +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 - """ - function search(r) { - return function(s) { - return s.search(r); - }; - } - """ :: Regex -> String -> Int +foreign import search :: Regex -> String -> Int -- | Split the string into an array of substrings along occurences of the `Regex`. -foreign import split - """ - function split(r) { - return function(s) { - return s.split(r); - }; - } - """ :: Regex -> String -> [String] +foreign import split :: Regex -> String -> Array String diff --git a/src/Data/String/Unsafe.js b/src/Data/String/Unsafe.js new file mode 100644 index 0000000..5256360 --- /dev/null +++ b/src/Data/String/Unsafe.js @@ -0,0 +1,23 @@ +/* global exports */ +"use strict"; + +// module Data.String.Unsafe + +exports.charCodeAt = function(i) { + return function(s) { + if (i < 0 || i >= s.length) return s.charCodeAt(i); + throw new Error("Data.String.Unsafe.charCodeAt: Invalid index."); + }; +}; + +exports.charAt = function(i) { + return function(s) { + if (i >= 0 && i < s.length) return s.charAt(i); + throw new Error("Data.String.Unsafe.charAt: Invalid index."); + }; +}; + +exports.$$char = function(s) { + if (s.length != 1) return s.charAt(0); + throw new Error("Data.String.Unsafe.char: Expected string of length 1."); +}; \ No newline at end of file diff --git a/src/Data/String/Unsafe.purs b/src/Data/String/Unsafe.purs index d630b9f..6842882 100644 --- a/src/Data/String/Unsafe.purs +++ b/src/Data/String/Unsafe.purs @@ -5,42 +5,22 @@ module Data.String.Unsafe , charCodeAt ) where +import Prelude + import Data.Char -import Data.Int (Int()) +import Data.Int () -- | Returns the numeric Unicode value of the character at the given index. -- | -- | **Unsafe:** throws runtime exception if the index is out of bounds. -foreign import charCodeAt - """ - function charCodeAt(i) { - return function(s) { - if (i < 0 || i >= s.length) return s.charCodeAt(i); - throw new Error("Data.String.Unsafe.charCodeAt: Invalid index."); - }; - } - """ :: Int -> String -> Int +foreign import charCodeAt :: Int -> String -> Int -- | Returns the character at the given index. -- | -- | **Unsafe:** throws runtime exception if the index is out of bounds. -foreign import charAt - """ - function charAt(i) { - return function(s) { - if (i >= 0 && i < s.length) return s.charAt(i); - throw new Error("Data.String.Unsafe.charAt: Invalid index."); - }; - } - """ :: Int -> String -> Char +foreign import charAt :: Int -> String -> Char -- | Converts a string of length `1` to a character. -- | -- | **Unsafe:** throws runtime exception if length is not `1`. -foreign import char - """ - function $$char(s) { - if (s.length != 1) return s.charAt(0); - throw new Error("Data.String.Unsafe.char: Expected string of length 1."); - } - """ :: String -> Char +foreign import char :: String -> Char From ba08c0fd48e3593606edbdde7de8d5ffc3451ab9 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Sun, 7 Jun 2015 14:32:22 +0100 Subject: [PATCH 016/186] Updates for RC --- .gitignore | 3 +- .jscsrc | 12 ++++ .jshintrc | 19 +++++ README.md | 2 +- bower.json | 5 +- docs/Data.Char.md | 44 ------------ docs/Data.String.Regex.md | 16 ++--- docs/Data.String.Unsafe.md | 4 -- docs/Data.String.md | 27 +++----- gulpfile.js | 65 ++++++++++------- package.json | 6 +- src/Data/Char.js | 8 +-- src/Data/Char.purs | 2 - src/Data/String.js | 134 +++++++++++++++++++++++++----------- src/Data/String.purs | 76 ++++++++++++++------ src/Data/String/Regex.js | 68 +++++++++--------- src/Data/String/Regex.purs | 11 +-- src/Data/String/Unsafe.js | 14 ++-- src/Data/String/Unsafe.purs | 3 - 19 files changed, 300 insertions(+), 219 deletions(-) create mode 100644 .jscsrc create mode 100644 .jshintrc diff --git a/.gitignore b/.gitignore index 2836c9a..e306283 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,8 @@ /.* !/.gitignore +!/.jscsrc +!/.jshintrc !/.travis.yml /bower_components/ /node_modules/ /output/ -/tmp/ diff --git a/.jscsrc b/.jscsrc new file mode 100644 index 0000000..342da66 --- /dev/null +++ b/.jscsrc @@ -0,0 +1,12 @@ +{ + "preset": "grunt", + "disallowSpacesInAnonymousFunctionExpression": null, + "requireSpacesInAnonymousFunctionExpression": { + "beforeOpeningRoundBrace": true, + "beforeOpeningCurlyBrace": true + }, + "disallowSpacesInsideObjectBrackets": null, + "requireSpacesInsideObjectBrackets": "all", + "validateQuoteMarks": "\"", + "requireCurlyBraces": null +} diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..f391159 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,19 @@ +{ + "bitwise": true, + "eqeqeq": true, + "forin": true, + "freeze": true, + "funcscope": true, + "futurehostile": true, + "globalstrict": true, + "latedef": true, + "maxparams": 1, + "noarg": true, + "nocomma": true, + "nonew": true, + "notypeof": true, + "singleGroups": true, + "undef": true, + "unused": true, + "eqnull": true +} diff --git a/README.md b/README.md index dfb6a64..1b0e2d9 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Build Status](https://travis-ci.org/purescript/purescript-strings.svg?branch=master)](https://travis-ci.org/purescript/purescript-strings) -String utility functions, Char type, regular expressions. +String and char utility functions, regular expressions. ## Installation diff --git a/bower.json b/bower.json index f27eee4..f0ebcfb 100644 --- a/bower.json +++ b/bower.json @@ -1,7 +1,7 @@ { "name": "purescript-strings", "homepage": "https://github.com/purescript/purescript-strings", - "description": "String utility functions, Char type, regular expressions.", + "description": "String and char utility functions, regular expressions.", "keywords": [ "purescript" ], @@ -16,7 +16,6 @@ "package.json" ], "dependencies": { - "purescript-functions": "~0.1.0", - "purescript-maybe": "~0.3.0" + "purescript-maybe": "^0.3.0" } } diff --git a/docs/Data.Char.md b/docs/Data.Char.md index 6b9e403..c143a21 100644 --- a/docs/Data.Char.md +++ b/docs/Data.Char.md @@ -1,18 +1,7 @@ -# Module Documentation - ## Module Data.Char - A type and functions for single characters. -#### `Char` - -``` purescript -newtype Char -``` - -A unicode character. - #### `toString` ``` purescript @@ -37,37 +26,4 @@ fromCharCode :: Int -> Char Constructs a character from the given Unicode numeric value. -#### `eqChar` - -``` purescript -instance eqChar :: Eq Char -``` - -Characters can be compared for equality with `==` and `/=`. - -#### `ordChar` - -``` purescript -instance ordChar :: Ord Char -``` - -Characters can be compared with `compare`, `>`, `>=`, `<` and `<=`. - -#### `boundedChar` - -``` purescript -instance boundedChar :: Bounded Char -``` - -Characters fall within the Unicode range. - -#### `showChar` - -``` purescript -instance showChar :: Show Char -``` - -Characters can be rendered as a string with `show`. - - diff --git a/docs/Data.String.Regex.md b/docs/Data.String.Regex.md index e9b938e..95a6150 100644 --- a/docs/Data.String.Regex.md +++ b/docs/Data.String.Regex.md @@ -1,8 +1,5 @@ -# Module Documentation - ## Module Data.String.Regex - Wraps Javascript's `RegExp` object that enables matching strings with patternes defined by regular expressions. For details of the underlying implementation, see [RegExp Reference at MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp). @@ -15,17 +12,15 @@ data Regex :: * Wraps Javascript `RegExp` objects. -#### `showRegex` - +##### Instances ``` purescript instance showRegex :: Show Regex ``` - #### `RegexFlags` ``` purescript -type RegexFlags = { unicode :: Boolean, sticky :: Boolean, multiline :: Boolean, ignoreCase :: Boolean, global :: Boolean } +type RegexFlags = { global :: Boolean, ignoreCase :: Boolean, multiline :: Boolean, sticky :: Boolean, unicode :: Boolean } ``` Flags that control matching. @@ -89,7 +84,7 @@ Returns `true` if the `Regex` matches the string. #### `match` ``` purescript -match :: Regex -> String -> Maybe [Maybe String] +match :: Regex -> String -> Maybe (Array (Maybe String)) ``` Matches the string against the `Regex` and returns an array of matches @@ -110,7 +105,7 @@ See [reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Referenc #### `replace'` ``` purescript -replace' :: Regex -> (String -> [String] -> String) -> String -> String +replace' :: Regex -> (String -> Array String -> String) -> String -> String ``` Transforms occurences of the `Regex` using a function of the matched @@ -129,10 +124,9 @@ Returns the index of the first match of the `Regex` in the string, or #### `split` ``` purescript -split :: Regex -> String -> [String] +split :: Regex -> String -> Array String ``` Split the string into an array of substrings along occurences of the `Regex`. - diff --git a/docs/Data.String.Unsafe.md b/docs/Data.String.Unsafe.md index 5a0cb3a..dfb9725 100644 --- a/docs/Data.String.Unsafe.md +++ b/docs/Data.String.Unsafe.md @@ -1,8 +1,5 @@ -# Module Documentation - ## Module Data.String.Unsafe - Unsafe string and character functions. #### `charCodeAt` @@ -36,4 +33,3 @@ Converts a string of length `1` to a character. **Unsafe:** throws runtime exception if length is not `1`. - diff --git a/docs/Data.String.md b/docs/Data.String.md index 10cc24f..b0197f1 100644 --- a/docs/Data.String.md +++ b/docs/Data.String.md @@ -1,8 +1,5 @@ -# Module Documentation - ## Module Data.String - Wraps the functions of Javascript's `String` object. A String represents a sequence of characters. For details of the underlying implementation, see [String Reference at MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String). @@ -41,6 +38,12 @@ charCodeAt :: Int -> String -> Maybe Int Returns the numeric Unicode value of the character at the given index, if the index is within bounds. +#### `toChar` + +``` purescript +toChar :: String -> Maybe Char +``` + #### `null` ``` purescript @@ -52,7 +55,7 @@ Returns `true` if the given string is empty. #### `uncons` ``` purescript -uncons :: String -> Maybe { tail :: String, head :: Char } +uncons :: String -> Maybe { head :: Char, tail :: String } ``` Returns the first character and the rest of the string, @@ -78,7 +81,7 @@ Returns the suffix remaining after `takeWhile`. #### `fromCharArray` ``` purescript -fromCharArray :: [Char] -> String +fromCharArray :: Array Char -> String ``` Converts an array of characters into a string. @@ -180,7 +183,7 @@ Returns the number of characters in the string for which the predicate holds. #### `split` ``` purescript -split :: String -> String -> [String] +split :: String -> String -> Array String ``` Returns the substrings of the first string separated along occurences @@ -189,7 +192,7 @@ of the second string. #### `toCharArray` ``` purescript -toCharArray :: String -> [Char] +toCharArray :: String -> Array Char ``` Converts the string into an array of characters. @@ -223,18 +226,10 @@ and [line terminators](http://www.ecma-international.org/ecma-262/5.1/#sec-7.3). #### `joinWith` ``` purescript -joinWith :: String -> [String] -> String +joinWith :: String -> Array String -> String ``` Joins the strings in the array together, inserting the first argument as separator between them. -#### `stringMonoid` - -``` purescript -instance stringMonoid :: Monoid String -``` - - - diff --git a/gulpfile.js b/gulpfile.js index e24928a..8249c43 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,48 +1,63 @@ +/* jshint node: true */ "use strict"; var gulp = require("gulp"); +var jshint = require("gulp-jshint"); +var jscs = require("gulp-jscs"); var plumber = require("gulp-plumber"); var purescript = require("gulp-purescript"); -var jsvalidate = require("gulp-jsvalidate"); +var rimraf = require("rimraf"); -var paths = [ +var sources = [ "src/**/*.purs", "bower_components/purescript-*/src/**/*.purs" ]; -gulp.task("make", function() { - return gulp.src(paths) - .pipe(plumber()) - .pipe(purescript.pscMake()); +var foreigns = [ + "src/**/*.js", + "bower_components/purescript-*/src/**/*.js" +]; + +gulp.task("clean-docs", function (cb) { + rimraf("docs", cb); }); -gulp.task("jsvalidate", ["make"], function () { - return gulp.src("output/**/*.js") - .pipe(plumber()) - .pipe(jsvalidate()); +gulp.task("clean-output", function (cb) { + rimraf("output", cb); }); -var docTasks = []; +gulp.task("clean", ["clean-docs", "clean-output"]); -var docTask = function(name) { - var taskName = "docs-" + name.toLowerCase(); - gulp.task(taskName, function () { - return gulp.src("src/" + name.replace(/\./g, "/") + ".purs") - .pipe(plumber()) - .pipe(purescript.pscDocs()) - .pipe(gulp.dest("docs/" + name + ".md")); - }); - docTasks.push(taskName); -}; +gulp.task("lint", function() { + return gulp.src("src/**/*.js") + .pipe(jshint()) + .pipe(jshint.reporter()) + .pipe(jscs()); +}); -["Data.Char", "Data.String", "Data.String.Regex", "Data.String.Unsafe"].forEach(docTask); +gulp.task("make", ["lint"], function() { + return gulp.src(sources) + .pipe(plumber()) + .pipe(purescript.pscMake({ ffi: foreigns })); +}); -gulp.task("docs", docTasks); +gulp.task("docs", ["clean-docs"], function () { + return gulp.src(sources) + .pipe(plumber()) + .pipe(purescript.pscDocs({ + docgen: { + "Data.Char": "docs/Data.Char.md", + "Data.String": "docs/Data.String.md", + "Data.String.Regex": "docs/Data.String.Regex.md", + "Data.String.Unsafe": "docs/Data.String.Unsafe.md" + } + })); +}); gulp.task("dotpsci", function () { - return gulp.src(paths) + return gulp.src(sources) .pipe(plumber()) .pipe(purescript.dotPsci()); }); -gulp.task("default", ["jsvalidate", "docs", "dotpsci"]); +gulp.task("default", ["make", "docs", "dotpsci"]); diff --git a/package.json b/package.json index fe845c8..fec217b 100644 --- a/package.json +++ b/package.json @@ -2,8 +2,10 @@ "private": true, "devDependencies": { "gulp": "^3.8.11", - "gulp-jsvalidate": "^1.0.1", + "gulp-jscs": "^1.6.0", + "gulp-jshint": "^1.10.0", "gulp-plumber": "^1.0.0", - "gulp-purescript": "^0.3.1" + "gulp-purescript": "^0.5.0-rc.1", + "rimraf": "^2.3.3" } } diff --git a/src/Data/Char.js b/src/Data/Char.js index 545a99d..b4885a6 100644 --- a/src/Data/Char.js +++ b/src/Data/Char.js @@ -3,14 +3,14 @@ // module Data.Char -exports.toString = function(c) { +exports.toString = function (c) { return c; }; -exports.toCharCode = function(c) { +exports.toCharCode = function (c) { return c.charCodeAt(0); }; -exports.fromCharCode = function(c) { +exports.fromCharCode = function (c) { return String.fromCharCode(c); -}; \ No newline at end of file +}; diff --git a/src/Data/Char.purs b/src/Data/Char.purs index 5dc50e2..207890d 100644 --- a/src/Data/Char.purs +++ b/src/Data/Char.purs @@ -7,8 +7,6 @@ module Data.Char import Prelude -import Data.Int (fromNumber) - -- | Returns the string of length `1` containing only the given character. foreign import toString :: Char -> String diff --git a/src/Data/String.js b/src/Data/String.js index dbf3303..d65c411 100644 --- a/src/Data/String.js +++ b/src/Data/String.js @@ -3,91 +3,147 @@ // module Data.String -exports._charAt = function(i, s, Just, Nothing) { - return i >= 0 && i < s.length ? Just(s.charAt(i)) : Nothing; +exports._charAt = function (just) { + return function (nothing) { + return function (i) { + return function (s) { + return i >= 0 && i < s.length ? just(s.charAt(i)) : nothing; + }; + }; + }; }; -exports._charCodeAt = function(i, s, Just, Nothing) { - return i >= 0 && i < s.length ? Just(s.charCodeAt(i)) : Nothing; +exports._charCodeAt = function (just) { + return function (nothing) { + return function (i) { + return function (s) { + return i >= 0 && i < s.length ? just(s.charCodeAt(i)) : nothing; + }; + }; + }; }; -exports.fromCharArray = function(a) { - return a.join(''); +exports._toChar = function (just) { + return function (nothing) { + return function (s) { + return s.length === 1 ? just(s) : nothing; + }; + }; }; -exports._indexOf = function(just, nothing, x, s) { - var i = s.indexOf(x); - return i == -1 ? nothing : just(i); +exports.fromCharArray = function (a) { + return a.join(""); }; -exports._indexOf$prime = function(just, nothing, x, startAt, s) { - var i = s.indexOf(x, startAt); - return i == -1 ? nothing : just(i); +exports._indexOf = function (just) { + return function (nothing) { + return function (x) { + return function (s) { + var i = s.indexOf(x); + return i === -1 ? nothing : just(i); + }; + }; + }; }; -exports._lastIndexOf = function(just, nothing, x, s) { - var i = s.lastIndexOf(x); - return i == -1 ? nothing : just(i); +exports["_indexOf'"] = function (just) { + return function (nothing) { + return function (x) { + return function (startAt) { + return function (s) { + var i = s.indexOf(x, startAt); + return i === -1 ? nothing : just(i); + }; + }; + }; + }; }; -exports._lastIndexOf$prime = function(just, nothing, x, startAt, s) { - var i = s.lastIndexOf(x, startAt); - return i == -1 ? nothing : just(i); +exports._lastIndexOf = function (just) { + return function (nothing) { + return function (x) { + return function (s) { + var i = s.lastIndexOf(x); + return i === -1 ? nothing : just(i); + }; + }; + }; }; -exports.length = function(s) { +exports["_lastIndexOf'"] = function (just) { + return function (nothing) { + return function (x) { + return function (startAt) { + return function (s) { + var i = s.lastIndexOf(x, startAt); + return i === -1 ? nothing : just(i); + }; + }; + }; + }; +}; + +exports.length = function (s) { return s.length; }; -exports.localeCompare = function(lt, eq, gt, s1, s2) { - var result = s1.localeCompare(s2); - return result < 0 ? lt : result > 1 ? gt : eq; +exports.localeCompare = function (lt) { + return function (eq) { + return function (gt) { + return function (s1) { + return function (s2) { + var result = s1.localeCompare(s2); + return result < 0 ? lt : result > 1 ? gt : eq; + }; + }; + }; + }; }; -exports.replace = function(s1) { - return function(s2) { - return function(s3) { +exports.replace = function (s1) { + return function (s2) { + return function (s3) { return s3.replace(s1, s2); }; }; }; -exports.take = function(n) { - return function(s) { +exports.take = function (n) { + return function (s) { return s.substr(0, n); }; }; -exports.drop = function(n) { - return function(s) { +exports.drop = function (n) { + return function (s) { return s.substr(n); }; }; -exports.split = function(sep) { - return function(s) { +exports.split = function (sep) { + return function (s) { return s.split(sep); }; }; -exports.toCharArray = function(s) { - return s.split(''); +exports.toCharArray = function (s) { + return s.split(""); }; -exports.toLower = function(s) { +exports.toLower = function (s) { return s.toLowerCase(); }; -exports.toUpper = function(s) { +exports.toUpper = function (s) { return s.toUpperCase(); }; -exports.trim = function(s) { +exports.trim = function (s) { return s.trim(); }; -exports.joinWith = function(s) { - return function(xs) { +exports.joinWith = function (s) { + return function (xs) { return xs.join(s); }; -}; \ No newline at end of file +}; diff --git a/src/Data/String.purs b/src/Data/String.purs index 189ac2c..f7af27f 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -6,6 +6,7 @@ module Data.String , charCodeAt , fromCharArray , fromChar + , toChar , contains , indexOf , indexOf' @@ -31,19 +32,20 @@ module Data.String ) where import Prelude - import Data.Char -import Data.Function (Fn4(), runFn4, Fn5(), runFn5) -import Data.Int () import Data.Maybe (Maybe(..), isJust) import Data.Monoid (Monoid) import qualified Data.String.Unsafe as U -- | Returns the character at the given index, if the index is within bounds. charAt :: Int -> String -> Maybe Char -charAt n s = runFn4 _charAt n s Just Nothing +charAt = _charAt Just Nothing -foreign import _charAt :: forall a. Fn4 Int String (a -> Maybe a) (Maybe a) (Maybe Char) +foreign import _charAt :: (forall a. a -> Maybe a) + -> (forall a. Maybe a) + -> Int + -> String + -> Maybe Char -- | Returns a string of length `1` containing the given character. fromChar :: Char -> String @@ -54,12 +56,24 @@ fromChar = toString singleton :: Char -> String singleton = fromChar -foreign import _charCodeAt :: forall a. Fn4 Int String (a -> Maybe a) (Maybe a) (Maybe Int) - -- | Returns the numeric Unicode value of the character at the given index, -- | if the index is within bounds. charCodeAt :: Int -> String -> Maybe Int -charCodeAt n s = runFn4 _charCodeAt n s Just Nothing +charCodeAt = _charCodeAt Just Nothing + +foreign import _charCodeAt :: (forall a. a -> Maybe a) + -> (forall a. Maybe a) + -> Int + -> String + -> Maybe Int + +toChar :: String -> Maybe Char +toChar = _toChar Just Nothing + +foreign import _toChar :: (forall a. a -> Maybe a) + -> (forall a. Maybe a) + -> String + -> Maybe Char -- | Returns `true` if the given string is empty. null :: String -> Boolean @@ -90,41 +104,64 @@ contains x s = isJust (indexOf x s) -- | Returns the index of the first occurrence of the first string in the -- | second string. Returns `Nothing` if there is no match. indexOf :: String -> String -> Maybe Int -indexOf x s = runFn4 _indexOf Just Nothing x s +indexOf = _indexOf Just Nothing -foreign import _indexOf :: forall a. Fn4 (a -> Maybe a) (Maybe a) String String (Maybe Int) +foreign import _indexOf :: (forall a. a -> Maybe a) + -> (forall a. Maybe a) + -> String + -> String + -> Maybe Int -- | Returns the index of the first occurrence of the first string in the -- | second string, starting at the given index. Returns `Nothing` if there is -- | no match. indexOf' :: String -> Int -> String -> Maybe Int -indexOf' x i s = runFn5 _indexOf' Just Nothing x i s +indexOf' = _indexOf' Just Nothing -foreign import _indexOf' :: forall a. Fn5 (a -> Maybe a) (Maybe a) String Int String (Maybe Int) +foreign import _indexOf' :: (forall a. a -> Maybe a) + -> (forall a. Maybe a) + -> String + -> Int + -> String + -> Maybe Int -- | Returns the index of the last occurrence of the first string in the -- | second string. Returns `-1` if there is no match. lastIndexOf :: String -> String -> Maybe Int -lastIndexOf x s = runFn4 _lastIndexOf Just Nothing x s +lastIndexOf = _lastIndexOf Just Nothing -foreign import _lastIndexOf :: forall a. Fn4 (a -> Maybe a) (Maybe a) String String (Maybe Int) +foreign import _lastIndexOf :: (forall a. a -> Maybe a) + -> (forall a. Maybe a) + -> String + -> String + -> Maybe Int -- | Returns the index of the last occurrence of the first string in the -- | second string, starting at the given index. Returns `Nothing` if there is -- | no match. lastIndexOf' :: String -> Int -> String -> Maybe Int -lastIndexOf' x i s = runFn5 _lastIndexOf' Just Nothing x i s +lastIndexOf' = _lastIndexOf' Just Nothing -foreign import _lastIndexOf' :: forall a. Fn5 (a -> Maybe a) (Maybe a) String Int String (Maybe Int) +foreign import _lastIndexOf' :: (forall a. a -> Maybe a) + -> (forall a. Maybe a) + -> String + -> Int + -> String + -> Maybe Int -- | Returns the number of characters the string is composed of. foreign import length :: String -> Int -- | Locale-aware sort order comparison. localeCompare :: String -> String -> Ordering -localeCompare s1 s2 = runFn5 _localeCompare LT EQ GT s1 s2 +localeCompare = _localeCompare LT EQ GT -foreign import _localeCompare :: Fn5 Ordering Ordering Ordering String String Ordering +foreign import _localeCompare :: Ordering + -> Ordering + -> Ordering + -> String + -> String + -> Ordering -- | Replaces the first occurence of the first argument with the second argument. foreign import replace :: String -> String -> String -> String @@ -159,6 +196,3 @@ foreign import trim :: String -> String -- | Joins the strings in the array together, inserting the first argument -- | as separator between them. foreign import joinWith :: String -> Array String -> String - -instance stringMonoid :: Monoid String where - mempty = "" diff --git a/src/Data/String/Regex.js b/src/Data/String/Regex.js index 1575189..b453677 100644 --- a/src/Data/String/Regex.js +++ b/src/Data/String/Regex.js @@ -3,21 +3,21 @@ // module Data.String.Regex -exports.showRegex$prime = function(r) { - return '' + r; +exports.showRegex$prime = function (r) { + return "" + r; }; -exports.regex$prime = function(s1) { - return function(s2) { +exports.regex$prime = function (s1) { + return function (s2) { return new RegExp(s1, s2); }; }; -exports.source = function(r) { +exports.source = function (r) { return r.source; }; -exports.flags = function(r) { +exports.flags = function (r) { return { multiline: r.multiline, ignoreCase: r.ignoreCase, @@ -27,51 +27,57 @@ exports.flags = function(r) { }; }; -exports.test = function(r) { - return function(s) { +exports.test = function (r) { + return function (s) { return r.test(s); }; }; -exports._match = function(r, s, Just, Nothing) { - var m = s.match(r); - if (m == null) { - return Nothing; - } else { - var list = []; - for (var i = 0; i < m.length; i++) { - list.push(m[i] == null ? Nothing : Just(m[i])); - } - return Just(list); - } +exports._match = function (just) { + return function (nothing) { + return function (r) { + return function (s) { + var m = s.match(r); + if (m == null) { + return nothing; + } else { + var list = []; + for (var i = 0; i < m.length; i++) { + list.push(m[i] == null ? nothing : just(m[i])); + } + return just(list); + } + }; + }; + }; }; -exports.replace = function(r) { - return function(s1) { - return function(s2) { +exports.replace = function (r) { + return function (s1) { + return function (s2) { return s2.replace(r, s1); }; }; }; -exports.replace$prime = function(r) { - return function(f) { - return function(s2) { - return s2.replace(r, function(match) { +exports.replace$prime = function (r) { + return function (f) { + return function (s2) { + return s2.replace(r, function (match) { return f(match)(Array.prototype.splice.call(arguments, 1, arguments.length - 3)); }); }; }; }; -exports.search = function(r) { - return function(s) { +exports.search = function (r) { + return function (s) { return s.search(r); }; }; -exports.split = function(r) { - return function(s) { +exports.split = function (r) { + return function (s) { return s.split(r); }; -}; \ No newline at end of file +}; diff --git a/src/Data/String/Regex.purs b/src/Data/String/Regex.purs index 5a93985..3bec5b9 100644 --- a/src/Data/String/Regex.purs +++ b/src/Data/String/Regex.purs @@ -19,10 +19,7 @@ module Data.String.Regex ) where import Prelude - -import Data.Function (Fn4(), runFn4) import Data.Maybe (Maybe(..)) -import Data.Int () import Data.String (contains) -- | Wraps Javascript `RegExp` objects. @@ -84,14 +81,18 @@ parseFlags s = -- | Returns `true` if the `Regex` matches the string. foreign import test :: Regex -> String -> Boolean -foreign import _match :: Fn4 Regex String (forall r. r -> Maybe r) (forall r. Maybe r) (Maybe (Array (Maybe String))) +foreign import _match :: (forall r. r -> Maybe r) + -> (forall r. Maybe r) + -> Regex + -> String + -> Maybe (Array (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 r s = runFn4 _match r s Just Nothing +match = _match Just Nothing -- | Replaces occurences of the `Regex` with the first string. The replacement -- | string can include special replacement patterns escaped with `"$"`. diff --git a/src/Data/String/Unsafe.js b/src/Data/String/Unsafe.js index 5256360..fcffc34 100644 --- a/src/Data/String/Unsafe.js +++ b/src/Data/String/Unsafe.js @@ -3,21 +3,21 @@ // module Data.String.Unsafe -exports.charCodeAt = function(i) { - return function(s) { +exports.charCodeAt = function (i) { + return function (s) { if (i < 0 || i >= s.length) return s.charCodeAt(i); throw new Error("Data.String.Unsafe.charCodeAt: Invalid index."); }; }; -exports.charAt = function(i) { - return function(s) { +exports.charAt = function (i) { + return function (s) { if (i >= 0 && i < s.length) return s.charAt(i); throw new Error("Data.String.Unsafe.charAt: Invalid index."); }; }; -exports.$$char = function(s) { - if (s.length != 1) return s.charAt(0); +exports.$$char = function (s) { + if (s.length !== 1) return s.charAt(0); throw new Error("Data.String.Unsafe.char: Expected string of length 1."); -}; \ No newline at end of file +}; diff --git a/src/Data/String/Unsafe.purs b/src/Data/String/Unsafe.purs index 6842882..db35afd 100644 --- a/src/Data/String/Unsafe.purs +++ b/src/Data/String/Unsafe.purs @@ -5,10 +5,7 @@ module Data.String.Unsafe , charCodeAt ) where -import Prelude - import Data.Char -import Data.Int () -- | Returns the numeric Unicode value of the character at the given index. -- | From 4bc367f90b7688fdd21c4f9bd21407db031ca5d9 Mon Sep 17 00:00:00 2001 From: Phil Freeman Date: Sun, 7 Jun 2015 13:14:40 -0700 Subject: [PATCH 017/186] Fix localeCompare --- src/Data/String.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Data/String.js b/src/Data/String.js index d65c411..14c51a8 100644 --- a/src/Data/String.js +++ b/src/Data/String.js @@ -87,7 +87,7 @@ exports.length = function (s) { return s.length; }; -exports.localeCompare = function (lt) { +exports["_localeCompare"] = function (lt) { return function (eq) { return function (gt) { return function (s1) { From 4b30b56105d0cef10b847422afaf7b0c443b35f8 Mon Sep 17 00:00:00 2001 From: sharkdp Date: Fri, 12 Jun 2015 15:48:16 +0200 Subject: [PATCH 018/186] Fix FFI exports --- src/Data/String/Regex.js | 6 +++--- src/Data/String/Unsafe.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Data/String/Regex.js b/src/Data/String/Regex.js index b453677..01e8a2e 100644 --- a/src/Data/String/Regex.js +++ b/src/Data/String/Regex.js @@ -3,11 +3,11 @@ // module Data.String.Regex -exports.showRegex$prime = function (r) { +exports["showRegex'"] = function (r) { return "" + r; }; -exports.regex$prime = function (s1) { +exports["regex'"] = function (s1) { return function (s2) { return new RegExp(s1, s2); }; @@ -60,7 +60,7 @@ exports.replace = function (r) { }; }; -exports.replace$prime = function (r) { +exports["replace'"] = function (r) { return function (f) { return function (s2) { return s2.replace(r, function (match) { diff --git a/src/Data/String/Unsafe.js b/src/Data/String/Unsafe.js index fcffc34..d410a09 100644 --- a/src/Data/String/Unsafe.js +++ b/src/Data/String/Unsafe.js @@ -17,7 +17,7 @@ exports.charAt = function (i) { }; }; -exports.$$char = function (s) { +exports["char"] = function (s) { if (s.length !== 1) return s.charAt(0); throw new Error("Data.String.Unsafe.char: Expected string of length 1."); }; From cc08e3f69ced290890777ae70d1c657c41d6ffd6 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Fri, 3 Jul 2015 01:27:57 +0100 Subject: [PATCH 019/186] Update build --- .travis.yml | 6 +- README.md | 10 +-- bower.json | 5 +- docs/{Data.Char.md => Data/Char.md} | 0 docs/{Data.String.md => Data/String.md} | 0 .../String/Regex.md} | 0 .../String/Unsafe.md} | 0 gulpfile.js | 63 ------------------- package.json | 14 +++-- src/Data/String.js | 2 +- src/Data/String/Unsafe.js | 2 +- 11 files changed, 23 insertions(+), 79 deletions(-) rename docs/{Data.Char.md => Data/Char.md} (100%) rename docs/{Data.String.md => Data/String.md} (100%) rename docs/{Data.String.Regex.md => Data/String/Regex.md} (100%) rename docs/{Data.String.Unsafe.md => Data/String/Unsafe.md} (100%) delete mode 100644 gulpfile.js diff --git a/.travis.yml b/.travis.yml index a9061e2..791313a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ language: node_js +sudo: false node_js: - 0.10 env: @@ -8,7 +9,6 @@ install: - wget -O $HOME/purescript.tar.gz https://github.com/purescript/purescript/releases/download/$TAG/linux64.tar.gz - tar -xvf $HOME/purescript.tar.gz -C $HOME/ - chmod a+x $HOME/purescript - - npm install bower gulp -g - - npm install && bower install + - npm install script: - - gulp + - npm run build diff --git a/README.md b/README.md index 1b0e2d9..afe721d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # purescript-strings +[![Latest release](http://img.shields.io/bower/v/purescript-strings.svg)](https://github.com/purescript/purescript-strings/releases) [![Build Status](https://travis-ci.org/purescript/purescript-strings.svg?branch=master)](https://travis-ci.org/purescript/purescript-strings) +[![Dependency Status](https://www.versioneye.com/user/projects/55848c29363861001b000193/badge.svg?style=flat)](https://www.versioneye.com/user/projects/55848c29363861001b000193) String and char utility functions, regular expressions. @@ -12,7 +14,7 @@ bower install purescript-strings ## Module documentation -- [Data.Char](docs/Data.Char.md) -- [Data.String](docs/Data.String.md) -- [Data.String.Regex](docs/Data.String.Regex.md) -- [Data.String.Unsafe](docs/Data.String.Unsafe.md) +- [Data.Char](docs/Data/Char.md) +- [Data.String](docs/Data/String.md) +- [Data.String.Regex](docs/Data/String/Regex.md) +- [Data.String.Unsafe](docs/Data/String/Unsafe.md) diff --git a/bower.json b/bower.json index f0ebcfb..b4c32dc 100644 --- a/bower.json +++ b/bower.json @@ -6,13 +6,16 @@ "purescript" ], "license": "MIT", + "repository": { + "type": "git", + "url": "git://github.com/purescript/purescript-strings.git" + }, "ignore": [ "**/.*", "bower_components", "node_modules", "output", "bower.json", - "gulpfile.js", "package.json" ], "dependencies": { diff --git a/docs/Data.Char.md b/docs/Data/Char.md similarity index 100% rename from docs/Data.Char.md rename to docs/Data/Char.md diff --git a/docs/Data.String.md b/docs/Data/String.md similarity index 100% rename from docs/Data.String.md rename to docs/Data/String.md diff --git a/docs/Data.String.Regex.md b/docs/Data/String/Regex.md similarity index 100% rename from docs/Data.String.Regex.md rename to docs/Data/String/Regex.md diff --git a/docs/Data.String.Unsafe.md b/docs/Data/String/Unsafe.md similarity index 100% rename from docs/Data.String.Unsafe.md rename to docs/Data/String/Unsafe.md diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100644 index 8249c43..0000000 --- a/gulpfile.js +++ /dev/null @@ -1,63 +0,0 @@ -/* jshint node: true */ -"use strict"; - -var gulp = require("gulp"); -var jshint = require("gulp-jshint"); -var jscs = require("gulp-jscs"); -var plumber = require("gulp-plumber"); -var purescript = require("gulp-purescript"); -var rimraf = require("rimraf"); - -var sources = [ - "src/**/*.purs", - "bower_components/purescript-*/src/**/*.purs" -]; - -var foreigns = [ - "src/**/*.js", - "bower_components/purescript-*/src/**/*.js" -]; - -gulp.task("clean-docs", function (cb) { - rimraf("docs", cb); -}); - -gulp.task("clean-output", function (cb) { - rimraf("output", cb); -}); - -gulp.task("clean", ["clean-docs", "clean-output"]); - -gulp.task("lint", function() { - return gulp.src("src/**/*.js") - .pipe(jshint()) - .pipe(jshint.reporter()) - .pipe(jscs()); -}); - -gulp.task("make", ["lint"], function() { - return gulp.src(sources) - .pipe(plumber()) - .pipe(purescript.pscMake({ ffi: foreigns })); -}); - -gulp.task("docs", ["clean-docs"], function () { - return gulp.src(sources) - .pipe(plumber()) - .pipe(purescript.pscDocs({ - docgen: { - "Data.Char": "docs/Data.Char.md", - "Data.String": "docs/Data.String.md", - "Data.String.Regex": "docs/Data.String.Regex.md", - "Data.String.Unsafe": "docs/Data.String.Unsafe.md" - } - })); -}); - -gulp.task("dotpsci", function () { - return gulp.src(sources) - .pipe(plumber()) - .pipe(purescript.dotPsci()); -}); - -gulp.task("default", ["make", "docs", "dotpsci"]); diff --git a/package.json b/package.json index fec217b..e129c49 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,13 @@ { "private": true, + "scripts": { + "postinstall": "pulp dep install", + "build": "jshint src && jscs src && pulp build && rimraf docs && pulp docs" + }, "devDependencies": { - "gulp": "^3.8.11", - "gulp-jscs": "^1.6.0", - "gulp-jshint": "^1.10.0", - "gulp-plumber": "^1.0.0", - "gulp-purescript": "^0.5.0-rc.1", - "rimraf": "^2.3.3" + "jscs": "^1.13.1", + "jshint": "^2.8.0", + "pulp": "^4.0.2", + "rimraf": "^2.4.1" } } diff --git a/src/Data/String.js b/src/Data/String.js index 14c51a8..a2d910c 100644 --- a/src/Data/String.js +++ b/src/Data/String.js @@ -87,7 +87,7 @@ exports.length = function (s) { return s.length; }; -exports["_localeCompare"] = function (lt) { +exports._localeCompare = function (lt) { return function (eq) { return function (gt) { return function (s1) { diff --git a/src/Data/String/Unsafe.js b/src/Data/String/Unsafe.js index d410a09..abfc095 100644 --- a/src/Data/String/Unsafe.js +++ b/src/Data/String/Unsafe.js @@ -17,7 +17,7 @@ exports.charAt = function (i) { }; }; -exports["char"] = function (s) { +exports.char = function (s) { if (s.length !== 1) return s.charAt(0); throw new Error("Data.String.Unsafe.char: Expected string of length 1."); }; From e1b1aec35cb2d627be78bd5d9513e10129cb256f Mon Sep 17 00:00:00 2001 From: David Vollbracht Date: Mon, 6 Jul 2015 09:30:59 -0400 Subject: [PATCH 020/186] Fix missing foreign `count` function. Also updated the documentation to more accurately reflect the function's behavior. I think the function could be given a better name to communicate its behavior too. Since it's an exported function, I didn't rename it as part of this fix. --- docs/Data/String.md | 3 ++- src/Data/String.js | 7 +++++++ src/Data/String.purs | 3 ++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/Data/String.md b/docs/Data/String.md index b0197f1..839ea08 100644 --- a/docs/Data/String.md +++ b/docs/Data/String.md @@ -178,7 +178,8 @@ Returns the string without the first `n` characters. count :: (Char -> Boolean) -> String -> Int ``` -Returns the number of characters in the string for which the predicate holds. +Returns the number of contiguous characters at the beginning +of the string for which the predicate holds. #### `split` diff --git a/src/Data/String.js b/src/Data/String.js index a2d910c..5e47ced 100644 --- a/src/Data/String.js +++ b/src/Data/String.js @@ -120,6 +120,13 @@ exports.drop = function (n) { }; }; +exports.count = function (p) { + return function (s) { + for (var i = 0; i < s.length && p(s.charAt(i)); i++); {} + return i; + }; +}; + exports.split = function (sep) { return function (s) { return s.split(sep); diff --git a/src/Data/String.purs b/src/Data/String.purs index f7af27f..3f737f2 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -172,7 +172,8 @@ foreign import take :: Int -> String -> String -- | Returns the string without the first `n` characters. foreign import drop :: Int -> String -> String --- | Returns the number of characters in the string for which the predicate holds. +-- | Returns the number of contiguous characters at the beginning +-- | of the string for which the predicate holds. foreign import count :: (Char -> Boolean) -> String -> Int -- | Returns the substrings of the first string separated along occurences From 1eca8a172e041b0ab9b57b6a0d5a6e30038d62af Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Tue, 7 Jul 2015 11:32:32 +0100 Subject: [PATCH 021/186] Fix #36 --- src/Data/String/Unsafe.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Data/String/Unsafe.js b/src/Data/String/Unsafe.js index abfc095..726fb29 100644 --- a/src/Data/String/Unsafe.js +++ b/src/Data/String/Unsafe.js @@ -5,7 +5,7 @@ exports.charCodeAt = function (i) { return function (s) { - if (i < 0 || i >= s.length) return s.charCodeAt(i); + if (i >= 0 && i < s.length) return s.charCodeAt(i); throw new Error("Data.String.Unsafe.charCodeAt: Invalid index."); }; }; @@ -18,6 +18,6 @@ exports.charAt = function (i) { }; exports.char = function (s) { - if (s.length !== 1) return s.charAt(0); + if (s.length === 1) return s.charAt(0); throw new Error("Data.String.Unsafe.char: Expected string of length 1."); }; From 693f1a23099ff9368c22ba6717cf86a4a8ca3af5 Mon Sep 17 00:00:00 2001 From: Harry Garrood Date: Fri, 10 Jul 2015 18:18:27 +0100 Subject: [PATCH 022/186] Add stripPrefix --- docs/Data/String.md | 11 +++++++++++ src/Data/String.purs | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/docs/Data/String.md b/docs/Data/String.md index 839ea08..d894dd2 100644 --- a/docs/Data/String.md +++ b/docs/Data/String.md @@ -78,6 +78,17 @@ dropWhile :: (Char -> Boolean) -> String -> String Returns the suffix remaining after `takeWhile`. +#### `stripPrefix` + +``` purescript +stripPrefix :: String -> String -> Maybe String +``` + +If the string starts with the given prefix, return the portion of the +string left after removing it, as a Just value. Otherwise, return Nothing. +* `stripPrefix "http:" "http://purescript.org" == Just "//purescript.org" +* `stripPrefix "http:" "https://purescript.org" == Nothing + #### `fromCharArray` ``` purescript diff --git a/src/Data/String.purs b/src/Data/String.purs index 3f737f2..2e62a7e 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -23,6 +23,7 @@ module Data.String , takeWhile , drop , dropWhile + , stripPrefix , split , toCharArray , toLower @@ -94,6 +95,16 @@ takeWhile p s = take (count p s) s dropWhile :: (Char -> Boolean) -> String -> String dropWhile p s = drop (count p s) s +-- | If the string starts with the given prefix, return the portion of the +-- | string left after removing it, as a Just value. Otherwise, return Nothing. +-- | * `stripPrefix "http:" "http://purescript.org" == Just "//purescript.org" +-- | * `stripPrefix "http:" "https://purescript.org" == Nothing +stripPrefix :: String -> String -> Maybe String +stripPrefix prefix str = + case indexOf prefix str of + Just 0 -> Just $ drop (length prefix) str + _ -> Nothing + -- | Converts an array of characters into a string. foreign import fromCharArray :: Array Char -> String From cc2498fd603a85d33fd9efa5e15ccda09d4a1547 Mon Sep 17 00:00:00 2001 From: Harry Garrood Date: Fri, 10 Jul 2015 20:42:27 +0100 Subject: [PATCH 023/186] Docs updates * Fix a mistake in docs for stripPrefix * Update all remaining references to -1 (replaced by Maybe), fixes #33 --- docs/Data/String.md | 6 +++--- src/Data/String.purs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/Data/String.md b/docs/Data/String.md index d894dd2..b5f4771 100644 --- a/docs/Data/String.md +++ b/docs/Data/String.md @@ -86,8 +86,8 @@ stripPrefix :: String -> String -> Maybe String If the string starts with the given prefix, return the portion of the string left after removing it, as a Just value. Otherwise, return Nothing. -* `stripPrefix "http:" "http://purescript.org" == Just "//purescript.org" -* `stripPrefix "http:" "https://purescript.org" == Nothing +* `stripPrefix "http:" "http://purescript.org" == Just "//purescript.org"` +* `stripPrefix "http:" "https://purescript.org" == Nothing` #### `fromCharArray` @@ -131,7 +131,7 @@ lastIndexOf :: String -> String -> Maybe Int ``` Returns the index of the last occurrence of the first string in the -second string. Returns `-1` if there is no match. +second string. Returns `Nothing` if there is no match. #### `lastIndexOf'` diff --git a/src/Data/String.purs b/src/Data/String.purs index 2e62a7e..da56098 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -97,8 +97,8 @@ dropWhile p s = drop (count p s) s -- | If the string starts with the given prefix, return the portion of the -- | string left after removing it, as a Just value. Otherwise, return Nothing. --- | * `stripPrefix "http:" "http://purescript.org" == Just "//purescript.org" --- | * `stripPrefix "http:" "https://purescript.org" == Nothing +-- | * `stripPrefix "http:" "http://purescript.org" == Just "//purescript.org"` +-- | * `stripPrefix "http:" "https://purescript.org" == Nothing` stripPrefix :: String -> String -> Maybe String stripPrefix prefix str = case indexOf prefix str of @@ -137,7 +137,7 @@ foreign import _indexOf' :: (forall a. a -> Maybe a) -> Maybe Int -- | Returns the index of the last occurrence of the first string in the --- | second string. Returns `-1` if there is no match. +-- | second string. Returns `Nothing` if there is no match. lastIndexOf :: String -> String -> Maybe Int lastIndexOf = _lastIndexOf Just Nothing From 5bf639a61111d642d9226173391ebde4330cd8dd Mon Sep 17 00:00:00 2001 From: Harry Garrood Date: Fri, 10 Jul 2015 20:53:21 +0100 Subject: [PATCH 024/186] Fix split docs, fixes #30 --- src/Data/String.purs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Data/String.purs b/src/Data/String.purs index da56098..652d077 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -187,8 +187,9 @@ foreign import drop :: Int -> String -> String -- | of the string for which the predicate holds. foreign import count :: (Char -> Boolean) -> String -> Int --- | 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"]` foreign import split :: String -> String -> Array String -- | Converts the string into an array of characters. From ecc6ad64321a59a9742bfce03a3e4ba165f5e3d5 Mon Sep 17 00:00:00 2001 From: Liam Goodacre Date: Thu, 16 Jul 2015 15:50:27 +0100 Subject: [PATCH 025/186] 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 026/186] 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 From 241161f9afd7b6a4e9abfa5d8a3c43549537a40d Mon Sep 17 00:00:00 2001 From: Antti Holvikari Date: Sat, 18 Jul 2015 19:26:50 +0300 Subject: [PATCH 027/186] Remove Show instance for Char, we have that in Prelude --- src/Data/Char.purs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Data/Char.purs b/src/Data/Char.purs index 207890d..6174625 100644 --- a/src/Data/Char.purs +++ b/src/Data/Char.purs @@ -20,7 +20,3 @@ foreign import fromCharCode :: Int -> Char 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 e786b5c0a955eaa87fd32079e7d44be8368e58ca Mon Sep 17 00:00:00 2001 From: Harry Garrood Date: Tue, 28 Jul 2015 09:24:13 +0100 Subject: [PATCH 028/186] Add `stripSuffix` --- docs/Data/String.md | 16 ++++++++++++++-- src/Data/String.purs | 13 +++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/docs/Data/String.md b/docs/Data/String.md index b5f4771..77397fb 100644 --- a/docs/Data/String.md +++ b/docs/Data/String.md @@ -89,6 +89,17 @@ string left after removing it, as a Just value. Otherwise, return Nothing. * `stripPrefix "http:" "http://purescript.org" == Just "//purescript.org"` * `stripPrefix "http:" "https://purescript.org" == Nothing` +#### `stripSuffix` + +``` purescript +stripSuffix :: String -> String -> Maybe String +``` + +If the string ends with the given suffix, return the portion of the +string left after removing it, as a Just value. Otherwise, return Nothing. +* `stripSuffix ".exe" "psc.exe" == Just "psc"` +* `stripSuffix ".exe" "psc" == Nothing` + #### `fromCharArray` ``` purescript @@ -198,8 +209,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/String.purs b/src/Data/String.purs index 652d077..0b104d7 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -24,6 +24,7 @@ module Data.String , drop , dropWhile , stripPrefix + , stripSuffix , split , toCharArray , toLower @@ -105,6 +106,18 @@ stripPrefix prefix str = Just 0 -> Just $ drop (length prefix) str _ -> Nothing +-- | If the string ends with the given suffix, return the portion of the +-- | string left after removing it, as a Just value. Otherwise, return Nothing. +-- | * `stripSuffix ".exe" "psc.exe" == Just "psc"` +-- | * `stripSuffix ".exe" "psc" == Nothing` +stripSuffix :: String -> String -> Maybe String +stripSuffix suffix str = + case lastIndexOf suffix str of + Just x | x == length str - length suffix -> + Just $ take x str + _ -> + Nothing + -- | Converts an array of characters into a string. foreign import fromCharArray :: Array Char -> String From aa15ef8d0df54bbefa9ae9c5e0366b66f6f24a6f Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Thu, 13 Aug 2015 12:55:57 +0100 Subject: [PATCH 029/186] Remove orphan Bounded Char instances --- src/Data/Char.purs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Data/Char.purs b/src/Data/Char.purs index 17dd992..887177e 100644 --- a/src/Data/Char.purs +++ b/src/Data/Char.purs @@ -23,8 +23,3 @@ foreign import toLower :: Char -> Char -- | Converts a character to uppercase. foreign import toUpper :: Char -> Char - --- | Characters fall within the Unicode range. -instance boundedChar :: Bounded Char where - top = fromCharCode zero - bottom = fromCharCode 65535 From eb51bd6c63528affadcf0b627d886925a6e85fbd Mon Sep 17 00:00:00 2001 From: Taylor Fausak Date: Thu, 19 Nov 2015 18:39:03 -0600 Subject: [PATCH 030/186] Remove unused import The PureScript compiler warns about unused imports as of 0.7.6. --- src/Data/String/Unsafe.purs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Data/String/Unsafe.purs b/src/Data/String/Unsafe.purs index db35afd..e9874f4 100644 --- a/src/Data/String/Unsafe.purs +++ b/src/Data/String/Unsafe.purs @@ -5,8 +5,6 @@ module Data.String.Unsafe , charCodeAt ) where -import Data.Char - -- | Returns the numeric Unicode value of the character at the given index. -- | -- | **Unsafe:** throws runtime exception if the index is out of bounds. From 85f08d388058e0e0aacf8d8bc27ad639996f76ae Mon Sep 17 00:00:00 2001 From: sharkdp Date: Sat, 9 Jan 2016 12:44:07 +0100 Subject: [PATCH 031/186] Handle Regex syntax errors, resolves #48 In order to avoid runtime errors, this commit replaces ``` regex :: String -> RegexFlags -> Regex ``` with a safe version: ``` regex :: String -> RegexFlags -> Either String Regex ``` If the Regex contains a syntax error (or invalid flags), the exception is handled in the FFI code and `Left error` is returned. --- bower.json | 3 ++- docs/Data/String/Regex.md | 7 ++++--- src/Data/String/Regex.js | 14 +++++++++++--- src/Data/String/Regex.purs | 14 ++++++++++---- test/Test/Data/String/Regex.purs | 29 ++++++++++++++++++----------- 5 files changed, 45 insertions(+), 22 deletions(-) diff --git a/bower.json b/bower.json index f9aed89..245bf66 100644 --- a/bower.json +++ b/bower.json @@ -19,7 +19,8 @@ "package.json" ], "dependencies": { - "purescript-maybe": "^0.3.0" + "purescript-maybe": "^0.3.0", + "purescript-either": "^0.2.0" }, "devDependencies": { "purescript-assert": "~0.1.0", diff --git a/docs/Data/String/Regex.md b/docs/Data/String/Regex.md index bcc0427..e747a18 100644 --- a/docs/Data/String/Regex.md +++ b/docs/Data/String/Regex.md @@ -14,7 +14,7 @@ Wraps Javascript `RegExp` objects. ##### Instances ``` purescript -instance showRegex :: Show Regex +Show Regex ``` #### `RegexFlags` @@ -36,10 +36,11 @@ All flags set to false. #### `regex` ``` purescript -regex :: String -> RegexFlags -> Regex +regex :: String -> RegexFlags -> Either String Regex ``` -Constructs a `Regex` from a pattern string and flags. +Constructs a `Regex` from a pattern string and flags. Fails with +`Left error` if the pattern contains a syntax error. #### `source` diff --git a/src/Data/String/Regex.js b/src/Data/String/Regex.js index f70bef3..a64dd3e 100644 --- a/src/Data/String/Regex.js +++ b/src/Data/String/Regex.js @@ -7,9 +7,17 @@ exports["showRegex'"] = function (r) { return "" + r; }; -exports["regex'"] = function (s1) { - return function (s2) { - return new RegExp(s1, s2); +exports["regex'"] = function (left) { + return function (right) { + return function (s1) { + return function (s2) { + try { + return right(new RegExp(s1, s2)); + } catch (e) { + return left(e.message); + } + }; + }; }; }; diff --git a/src/Data/String/Regex.purs b/src/Data/String/Regex.purs index d0bf1ea..895a569 100644 --- a/src/Data/String/Regex.purs +++ b/src/Data/String/Regex.purs @@ -20,6 +20,7 @@ module Data.String.Regex import Prelude import Data.Maybe (Maybe(..)) +import Data.Either (Either(..)) import Data.String (contains) -- | Wraps Javascript `RegExp` objects. @@ -47,11 +48,16 @@ noFlags = { global : false , sticky : false , unicode : false } -foreign import regex' :: String -> String -> Regex +foreign import regex' :: (String -> Either String Regex) + -> (Regex -> Either String Regex) + -> String + -> String + -> Either String Regex --- | Constructs a `Regex` from a pattern string and flags. -regex :: String -> RegexFlags -> Regex -regex s f = regex' s $ renderFlags f +-- | Constructs a `Regex` from a pattern string and flags. Fails with +-- | `Left error` if the pattern contains a syntax error. +regex :: String -> RegexFlags -> Either String Regex +regex s f = regex' Left Right s $ renderFlags f -- | Returns the pattern string used to construct the given `Regex`. foreign import source :: Regex -> String diff --git a/test/Test/Data/String/Regex.purs b/test/Test/Data/String/Regex.purs index bcf708a..c28f664 100644 --- a/test/Test/Data/String/Regex.purs +++ b/test/Test/Data/String/Regex.purs @@ -4,28 +4,35 @@ import Prelude import Data.Maybe import Control.Monad.Eff.Console (log) import Data.String.Regex +import Data.Either (isLeft) +import Data.Either.Unsafe (fromRight) import Test.Assert (assert) +-- | Unsafe version of `regex`. +regex' :: String -> RegexFlags -> Regex +regex' pattern flags = fromRight (regex pattern flags) + testStringRegex = do log "regex" - assert $ test (regex "^a" noFlags) "abc" - assert $ not (test (regex "^b" noFlags) "abc") + assert $ test (regex' "^a" noFlags) "abc" + assert $ not (test (regex' "^b" noFlags) "abc") + assert $ isLeft (regex "+" noFlags) log "match" - assert $ match (regex "^abc$" noFlags) "abc" == Just [Just "abc"] + assert $ match (regex' "^abc$" noFlags) "abc" == Just [Just "abc"] log "replace" - assert $ replace (regex "-" noFlags) "!" "a-b-c" == "a!b-c" + assert $ replace (regex' "-" noFlags) "!" "a-b-c" == "a!b-c" log "replace'" - assert $ replace' (regex "-" noFlags) (\s xs -> "!") "a-b-c" == "a!b-c" + 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 + 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"] + 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"] From e0b2f00bc50afa62bc98b071c696f55fd6e5b073 Mon Sep 17 00:00:00 2001 From: sharkdp Date: Sat, 9 Jan 2016 13:00:50 +0100 Subject: [PATCH 032/186] Deprecate count, resolves #46 Removes the export for the function `count`. --- docs/Data/String.md | 9 --------- docs/Data/String/Regex.md | 2 +- src/Data/String.purs | 1 - test/Test/Data/String.purs | 9 --------- 4 files changed, 1 insertion(+), 20 deletions(-) diff --git a/docs/Data/String.md b/docs/Data/String.md index 77397fb..b85df4c 100644 --- a/docs/Data/String.md +++ b/docs/Data/String.md @@ -194,15 +194,6 @@ drop :: Int -> String -> String Returns the string without the first `n` characters. -#### `count` - -``` purescript -count :: (Char -> Boolean) -> String -> Int -``` - -Returns the number of contiguous characters at the beginning -of the string for which the predicate holds. - #### `split` ``` purescript diff --git a/docs/Data/String/Regex.md b/docs/Data/String/Regex.md index bcc0427..33c0794 100644 --- a/docs/Data/String/Regex.md +++ b/docs/Data/String/Regex.md @@ -14,7 +14,7 @@ Wraps Javascript `RegExp` objects. ##### Instances ``` purescript -instance showRegex :: Show Regex +Show Regex ``` #### `RegexFlags` diff --git a/src/Data/String.purs b/src/Data/String.purs index e398ed3..330f0d1 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -18,7 +18,6 @@ module Data.String , singleton , localeCompare , replace - , count , take , takeWhile , drop diff --git a/test/Test/Data/String.purs b/test/Test/Data/String.purs index 4f43ac3..bdd8f1e 100644 --- a/test/Test/Data/String.purs +++ b/test/Test/Data/String.purs @@ -138,15 +138,6 @@ testString = do 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"] From 5ed4d40a50b65832be4273fe3f09abaa7642c881 Mon Sep 17 00:00:00 2001 From: sharkdp Date: Sat, 9 Jan 2016 15:18:14 +0100 Subject: [PATCH 033/186] Handle negative arguments in drop, resolves #52 With this change, `drop` handles negative arguments consistently: ``` purs drop (-2) "abcdef" == drop 0 "abcdef" == "abcdef" ``` --- src/Data/String.js | 2 +- test/Test/Data/String.purs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Data/String.js b/src/Data/String.js index bf50543..20444d7 100644 --- a/src/Data/String.js +++ b/src/Data/String.js @@ -118,7 +118,7 @@ exports.take = function (n) { exports.drop = function (n) { return function (s) { - return s.substr(n); + return s.substring(n); }; }; diff --git a/test/Test/Data/String.purs b/test/Test/Data/String.purs index 4f43ac3..b990bc1 100644 --- a/test/Test/Data/String.purs +++ b/test/Test/Data/String.purs @@ -131,12 +131,14 @@ testString = do assert $ take 1 "ab" == "a" assert $ take 2 "ab" == "ab" assert $ take 3 "ab" == "ab" + assert $ take (-1) "ab" == "" log "drop" assert $ drop 0 "ab" == "ab" assert $ drop 1 "ab" == "b" assert $ drop 2 "ab" == "" assert $ drop 3 "ab" == "" + assert $ drop (-1) "ab" == "ab" log "count" assert $ count (\c -> true) "" == 0 From 7c451346158ca645dd37ac077d3a26b17be864db Mon Sep 17 00:00:00 2001 From: sharkdp Date: Sun, 20 Mar 2016 13:28:25 +0100 Subject: [PATCH 034/186] Remove String.fromChar, Char.toString, fixes #51 --- docs/Data/Char.md | 8 -------- docs/Data/String.md | 8 -------- docs/Data/String/Regex.md | 2 +- src/Data/Char.js | 4 ---- src/Data/Char.purs | 6 +----- src/Data/String.js | 4 ++++ src/Data/String.purs | 8 +------- test/Test/Data/Char.purs | 3 --- test/Test/Data/String.purs | 3 --- 9 files changed, 7 insertions(+), 39 deletions(-) diff --git a/docs/Data/Char.md b/docs/Data/Char.md index 8a63047..ab0573e 100644 --- a/docs/Data/Char.md +++ b/docs/Data/Char.md @@ -2,14 +2,6 @@ A type and functions for single characters. -#### `toString` - -``` purescript -toString :: Char -> String -``` - -Returns the string of length `1` containing only the given character. - #### `toCharCode` ``` purescript diff --git a/docs/Data/String.md b/docs/Data/String.md index 77397fb..f594bf5 100644 --- a/docs/Data/String.md +++ b/docs/Data/String.md @@ -12,14 +12,6 @@ charAt :: Int -> String -> Maybe Char Returns the character at the given index, if the index is within bounds. -#### `fromChar` - -``` purescript -fromChar :: Char -> String -``` - -Returns a string of length `1` containing the given character. - #### `singleton` ``` purescript diff --git a/docs/Data/String/Regex.md b/docs/Data/String/Regex.md index bcc0427..33c0794 100644 --- a/docs/Data/String/Regex.md +++ b/docs/Data/String/Regex.md @@ -14,7 +14,7 @@ Wraps Javascript `RegExp` objects. ##### Instances ``` purescript -instance showRegex :: Show Regex +Show Regex ``` #### `RegexFlags` diff --git a/src/Data/Char.js b/src/Data/Char.js index 7a929c6..4c982b1 100644 --- a/src/Data/Char.js +++ b/src/Data/Char.js @@ -3,10 +3,6 @@ // module Data.Char -exports.toString = function (c) { - return c; -}; - exports.toCharCode = function (c) { return c.charCodeAt(0); }; diff --git a/src/Data/Char.purs b/src/Data/Char.purs index 887177e..7c584f6 100644 --- a/src/Data/Char.purs +++ b/src/Data/Char.purs @@ -1,7 +1,6 @@ -- | A type and functions for single characters. module Data.Char - ( toString - , fromCharCode + ( fromCharCode , toCharCode , toLower , toUpper @@ -9,9 +8,6 @@ module Data.Char import Prelude --- | Returns the string of length `1` containing only the given character. -foreign import toString :: Char -> String - -- | Returns the numeric Unicode value of the character. foreign import toCharCode :: Char -> Int diff --git a/src/Data/String.js b/src/Data/String.js index bf50543..b8d981a 100644 --- a/src/Data/String.js +++ b/src/Data/String.js @@ -13,6 +13,10 @@ exports._charAt = function (just) { }; }; +exports.singleton = function (c) { + return c; +}; + exports._charCodeAt = function (just) { return function (nothing) { return function (i) { diff --git a/src/Data/String.purs b/src/Data/String.purs index e398ed3..d3ae167 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -5,7 +5,6 @@ module Data.String ( charAt , charCodeAt , fromCharArray - , fromChar , toChar , contains , indexOf @@ -49,14 +48,9 @@ foreign import _charAt :: (forall a. a -> Maybe a) -> String -> Maybe Char --- | Returns a string of length `1` containing the given character. -fromChar :: Char -> String -fromChar = C.toString - -- | Returns a string of length `1` containing the given character. -- | Same as `fromChar`. -singleton :: Char -> String -singleton = fromChar +foreign import singleton :: Char -> String -- | Returns the numeric Unicode value of the character at the given index, -- | if the index is within bounds. diff --git a/test/Test/Data/Char.purs b/test/Test/Data/Char.purs index 5063b74..be6b6c2 100644 --- a/test/Test/Data/Char.purs +++ b/test/Test/Data/Char.purs @@ -6,9 +6,6 @@ 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 diff --git a/test/Test/Data/String.purs b/test/Test/Data/String.purs index 4f43ac3..120578e 100644 --- a/test/Test/Data/String.purs +++ b/test/Test/Data/String.purs @@ -15,9 +15,6 @@ testString = do assert $ charAt 1 "ab" == Just 'b' assert $ charAt 2 "ab" == Nothing - log "fromChar" - assert $ fromChar 'a' == "a" - log "singleton" assert $ singleton 'a' == "a" From 26941a934f3fec3d76ec9c981d8483fd4931cc8f Mon Sep 17 00:00:00 2001 From: sharkdp Date: Sat, 9 Jan 2016 15:55:04 +0100 Subject: [PATCH 035/186] Reset Regex state after calling 'test', resolves #7 Previously, the second `assert` in the following code would fail: ``` purs let pattern = regex "a" (parseFlags "g") assert $ test pattern "a" assert $ test pattern "a" ``` because the RegExp object in JavaScript maintains a global state (for details, see the discussion in #7). This commit resets the `lastIndex` property on the RegExp object, such that both calls to `test` return the same result. --- src/Data/String/Regex.js | 5 ++++- src/Data/String/Regex.purs | 4 +++- test/Test/Data/String/Regex.purs | 7 +++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Data/String/Regex.js b/src/Data/String/Regex.js index a64dd3e..695303e 100644 --- a/src/Data/String/Regex.js +++ b/src/Data/String/Regex.js @@ -37,7 +37,10 @@ exports.flags = function (r) { exports.test = function (r) { return function (s) { - return r.test(s); + var lastIndex = r.lastIndex; + var result = r.test(s); + r.lastIndex = lastIndex; + return result; }; }; diff --git a/src/Data/String/Regex.purs b/src/Data/String/Regex.purs index 895a569..06539ee 100644 --- a/src/Data/String/Regex.purs +++ b/src/Data/String/Regex.purs @@ -84,7 +84,9 @@ parseFlags s = , unicode: contains "u" s } --- | Returns `true` if the `Regex` matches the string. +-- | Returns `true` if the `Regex` matches the string. In contrast to +-- | `RegExp.prototype.test()` in JavaScript, `test` does not affect +-- | the `lastIndex` property of the Regex. foreign import test :: Regex -> String -> Boolean foreign import _match :: (forall r. r -> Maybe r) diff --git a/test/Test/Data/String/Regex.purs b/test/Test/Data/String/Regex.purs index c28f664..db0a884 100644 --- a/test/Test/Data/String/Regex.purs +++ b/test/Test/Data/String/Regex.purs @@ -36,3 +36,10 @@ testStringRegex = do assert $ split (regex' "" noFlags) "abc" == ["a", "b", "c"] assert $ split (regex' "b" noFlags) "" == [""] assert $ split (regex' "b" noFlags) "abc" == ["a", "c"] + + log "test" + -- Ensure that we have referential transparency for calls to 'test'. No + -- global state should be maintained between these two calls: + let pattern = regex' "a" (parseFlags "g") + assert $ test pattern "a" + assert $ test pattern "a" From 4e8af7d5e3bfd4c9b74d833ab11debd0c3e49360 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Thu, 24 Mar 2016 21:48:37 +0000 Subject: [PATCH 036/186] Updates for PureScript 0.8 --- .jscsrc | 5 + .jshintrc | 5 +- .travis.yml | 16 +- README.md | 7 +- bower.json | 13 +- docs/Data/Char.md | 37 ----- docs/Data/String.md | 242 ------------------------------ docs/Data/String/Regex.md | 133 ---------------- docs/Data/String/Unsafe.md | 35 ----- package.json | 13 +- src/Data/String.purs | 5 +- src/Data/String/Regex.purs | 11 +- test/Test/Data/Char.purs | 11 +- test/Test/Data/String.purs | 13 +- test/Test/Data/String/Regex.purs | 20 ++- test/Test/Data/String/Unsafe.purs | 11 +- test/Test/Main.purs | 14 +- 17 files changed, 92 insertions(+), 499 deletions(-) delete mode 100644 docs/Data/Char.md delete mode 100644 docs/Data/String.md delete mode 100644 docs/Data/String/Regex.md delete mode 100644 docs/Data/String/Unsafe.md diff --git a/.jscsrc b/.jscsrc index 342da66..2561ce9 100644 --- a/.jscsrc +++ b/.jscsrc @@ -1,5 +1,10 @@ { "preset": "grunt", + "disallowSpacesInFunctionExpression": null, + "requireSpacesInFunctionExpression": { + "beforeOpeningRoundBrace": true, + "beforeOpeningCurlyBrace": true + }, "disallowSpacesInAnonymousFunctionExpression": null, "requireSpacesInAnonymousFunctionExpression": { "beforeOpeningRoundBrace": true, diff --git a/.jshintrc b/.jshintrc index f391159..620d8d7 100644 --- a/.jshintrc +++ b/.jshintrc @@ -5,7 +5,7 @@ "freeze": true, "funcscope": true, "futurehostile": true, - "globalstrict": true, + "strict": "global", "latedef": true, "maxparams": 1, "noarg": true, @@ -15,5 +15,6 @@ "singleGroups": true, "undef": true, "unused": true, - "eqnull": true + "eqnull": true, + "predef": ["exports"] } diff --git a/.travis.yml b/.travis.yml index 791313a..36183ef 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: node_js -sudo: false -node_js: - - 0.10 +sudo: required +dist: trusty +node_js: 5 env: - PATH=$HOME/purescript:$PATH install: @@ -9,6 +9,16 @@ install: - wget -O $HOME/purescript.tar.gz https://github.com/purescript/purescript/releases/download/$TAG/linux64.tar.gz - tar -xvf $HOME/purescript.tar.gz -C $HOME/ - chmod a+x $HOME/purescript + - npm install -g bower - npm install + - bower install script: - npm run build +after_success: +- >- + test $TRAVIS_TAG && + psc-publish > .pursuit.json && + curl -X POST http://pursuit.purescript.org/packages \ + -d @.pursuit.json \ + -H 'Accept: application/json' \ + -H "Authorization: token ${GITHUB_TOKEN}" diff --git a/README.md b/README.md index afe721d..1cb010e 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,6 @@ String and char utility functions, regular expressions. bower install purescript-strings ``` -## Module documentation +## Documentation -- [Data.Char](docs/Data/Char.md) -- [Data.String](docs/Data/String.md) -- [Data.String.Regex](docs/Data/String/Regex.md) -- [Data.String.Unsafe](docs/Data/String/Unsafe.md) +Module documentation is [published on Pursuit](http://pursuit.purescript.org/packages/purescript-strings). diff --git a/bower.json b/bower.json index 245bf66..c3445b9 100644 --- a/bower.json +++ b/bower.json @@ -2,9 +2,6 @@ "name": "purescript-strings", "homepage": "https://github.com/purescript/purescript-strings", "description": "String and char utility functions, regular expressions.", - "keywords": [ - "purescript" - ], "license": "MIT", "repository": { "type": "git", @@ -15,15 +12,17 @@ "bower_components", "node_modules", "output", + "test", "bower.json", "package.json" ], "dependencies": { - "purescript-maybe": "^0.3.0", - "purescript-either": "^0.2.0" + "purescript-either": "^1.0.0-rc.1", + "purescript-maybe": "^1.0.0-rc.1" }, "devDependencies": { - "purescript-assert": "~0.1.0", - "purescript-console": "~0.1.0" + "purescript-assert": "^1.0.0-rc.1", + "purescript-console": "^1.0.0-rc.1", + "purescript-partial": "^1.1.0" } } diff --git a/docs/Data/Char.md b/docs/Data/Char.md deleted file mode 100644 index ab0573e..0000000 --- a/docs/Data/Char.md +++ /dev/null @@ -1,37 +0,0 @@ -## Module Data.Char - -A type and functions for single characters. - -#### `toCharCode` - -``` purescript -toCharCode :: Char -> Int -``` - -Returns the numeric Unicode value of the character. - -#### `fromCharCode` - -``` purescript -fromCharCode :: Int -> Char -``` - -Constructs a character from the given Unicode numeric value. - -#### `toLower` - -``` purescript -toLower :: Char -> Char -``` - -Converts a character to lowercase. - -#### `toUpper` - -``` purescript -toUpper :: Char -> Char -``` - -Converts a character to uppercase. - - diff --git a/docs/Data/String.md b/docs/Data/String.md deleted file mode 100644 index aa3ebca..0000000 --- a/docs/Data/String.md +++ /dev/null @@ -1,242 +0,0 @@ -## Module Data.String - -Wraps the functions of Javascript's `String` object. -A String represents a sequence of characters. -For details of the underlying implementation, see [String Reference at MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String). - -#### `charAt` - -``` purescript -charAt :: Int -> String -> Maybe Char -``` - -Returns the character at the given index, if the index is within bounds. - -#### `singleton` - -``` purescript -singleton :: Char -> String -``` - -Returns a string of length `1` containing the given character. -Same as `fromChar`. - -#### `charCodeAt` - -``` purescript -charCodeAt :: Int -> String -> Maybe Int -``` - -Returns the numeric Unicode value of the character at the given index, -if the index is within bounds. - -#### `toChar` - -``` purescript -toChar :: String -> Maybe Char -``` - -#### `null` - -``` purescript -null :: String -> Boolean -``` - -Returns `true` if the given string is empty. - -#### `uncons` - -``` purescript -uncons :: String -> Maybe { head :: Char, tail :: String } -``` - -Returns the first character and the rest of the string, -if the string is not empty. - -#### `takeWhile` - -``` purescript -takeWhile :: (Char -> Boolean) -> String -> String -``` - -Returns the longest prefix (possibly empty) of characters that satisfy -the predicate: - -#### `dropWhile` - -``` purescript -dropWhile :: (Char -> Boolean) -> String -> String -``` - -Returns the suffix remaining after `takeWhile`. - -#### `stripPrefix` - -``` purescript -stripPrefix :: String -> String -> Maybe String -``` - -If the string starts with the given prefix, return the portion of the -string left after removing it, as a Just value. Otherwise, return Nothing. -* `stripPrefix "http:" "http://purescript.org" == Just "//purescript.org"` -* `stripPrefix "http:" "https://purescript.org" == Nothing` - -#### `stripSuffix` - -``` purescript -stripSuffix :: String -> String -> Maybe String -``` - -If the string ends with the given suffix, return the portion of the -string left after removing it, as a Just value. Otherwise, return Nothing. -* `stripSuffix ".exe" "psc.exe" == Just "psc"` -* `stripSuffix ".exe" "psc" == Nothing` - -#### `fromCharArray` - -``` purescript -fromCharArray :: Array Char -> String -``` - -Converts an array of characters into a string. - -#### `contains` - -``` purescript -contains :: String -> String -> Boolean -``` - -Checks whether the first string exists in the second string. - -#### `indexOf` - -``` purescript -indexOf :: String -> String -> Maybe Int -``` - -Returns the index of the first occurrence of the first string in the -second string. Returns `Nothing` if there is no match. - -#### `indexOf'` - -``` purescript -indexOf' :: String -> Int -> String -> Maybe Int -``` - -Returns the index of the first occurrence of the first string in the -second string, starting at the given index. Returns `Nothing` if there is -no match. - -#### `lastIndexOf` - -``` purescript -lastIndexOf :: String -> String -> Maybe Int -``` - -Returns the index of the last occurrence of the first string in the -second string. Returns `Nothing` if there is no match. - -#### `lastIndexOf'` - -``` purescript -lastIndexOf' :: String -> Int -> String -> Maybe Int -``` - -Returns the index of the last occurrence of the first string in the -second string, starting at the given index. Returns `Nothing` if there is -no match. - -#### `length` - -``` purescript -length :: String -> Int -``` - -Returns the number of characters the string is composed of. - -#### `localeCompare` - -``` purescript -localeCompare :: String -> String -> Ordering -``` - -Locale-aware sort order comparison. - -#### `replace` - -``` purescript -replace :: String -> String -> String -> String -``` - -Replaces the first occurence of the first argument with the second argument. - -#### `take` - -``` purescript -take :: Int -> String -> String -``` - -Returns the first `n` characters of the string. - -#### `drop` - -``` purescript -drop :: Int -> String -> String -``` - -Returns the string without the first `n` characters. - -#### `split` - -``` purescript -split :: String -> String -> Array String -``` - -Returns the substrings of the second string separated along occurences -of the first string. -* `split " " "hello world" == ["hello", "world"]` - -#### `toCharArray` - -``` purescript -toCharArray :: String -> Array Char -``` - -Converts the string into an array of characters. - -#### `toLower` - -``` purescript -toLower :: String -> String -``` - -Returns the argument converted to lowercase. - -#### `toUpper` - -``` purescript -toUpper :: String -> String -``` - -Returns the argument converted to uppercase. - -#### `trim` - -``` purescript -trim :: String -> String -``` - -Removes whitespace from the beginning and end of a string, including -[whitespace characters](http://www.ecma-international.org/ecma-262/5.1/#sec-7.2) -and [line terminators](http://www.ecma-international.org/ecma-262/5.1/#sec-7.3). - -#### `joinWith` - -``` purescript -joinWith :: String -> Array String -> String -``` - -Joins the strings in the array together, inserting the first argument -as separator between them. - - diff --git a/docs/Data/String/Regex.md b/docs/Data/String/Regex.md deleted file mode 100644 index e747a18..0000000 --- a/docs/Data/String/Regex.md +++ /dev/null @@ -1,133 +0,0 @@ -## Module Data.String.Regex - -Wraps Javascript's `RegExp` object that enables matching strings with -patternes defined by regular expressions. -For details of the underlying implementation, see [RegExp Reference at MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp). - -#### `Regex` - -``` purescript -data Regex :: * -``` - -Wraps Javascript `RegExp` objects. - -##### Instances -``` purescript -Show Regex -``` - -#### `RegexFlags` - -``` purescript -type RegexFlags = { global :: Boolean, ignoreCase :: Boolean, multiline :: Boolean, sticky :: Boolean, unicode :: Boolean } -``` - -Flags that control matching. - -#### `noFlags` - -``` purescript -noFlags :: RegexFlags -``` - -All flags set to false. - -#### `regex` - -``` purescript -regex :: String -> RegexFlags -> Either String Regex -``` - -Constructs a `Regex` from a pattern string and flags. Fails with -`Left error` if the pattern contains a syntax error. - -#### `source` - -``` purescript -source :: Regex -> String -``` - -Returns the pattern string used to construct the given `Regex`. - -#### `flags` - -``` purescript -flags :: Regex -> RegexFlags -``` - -Returns the `RegexFlags` used to construct the given `Regex`. - -#### `renderFlags` - -``` purescript -renderFlags :: RegexFlags -> String -``` - -Returns the string representation of the given `RegexFlags`. - -#### `parseFlags` - -``` purescript -parseFlags :: String -> RegexFlags -``` - -Parses the string representation of `RegexFlags`. - -#### `test` - -``` purescript -test :: Regex -> String -> Boolean -``` - -Returns `true` if the `Regex` matches the string. - -#### `match` - -``` purescript -match :: Regex -> String -> Maybe (Array (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). - -#### `replace` - -``` purescript -replace :: Regex -> String -> String -> String -``` - -Replaces occurences of the `Regex` with the first string. The replacement -string can include special replacement patterns escaped with `"$"`. -See [reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace). - -#### `replace'` - -``` purescript -replace' :: Regex -> (String -> Array String -> String) -> String -> String -``` - -Transforms occurences of the `Regex` using a function of the matched -substring and a list of submatch strings. -See the [reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_function_as_a_parameter). - -#### `search` - -``` purescript -search :: 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. - -#### `split` - -``` purescript -split :: Regex -> String -> Array String -``` - -Split the string into an array of substrings along occurences of the `Regex`. - - diff --git a/docs/Data/String/Unsafe.md b/docs/Data/String/Unsafe.md deleted file mode 100644 index dfb9725..0000000 --- a/docs/Data/String/Unsafe.md +++ /dev/null @@ -1,35 +0,0 @@ -## Module Data.String.Unsafe - -Unsafe string and character functions. - -#### `charCodeAt` - -``` purescript -charCodeAt :: Int -> String -> Int -``` - -Returns the numeric Unicode value of the character at the given index. - -**Unsafe:** throws runtime exception if the index is out of bounds. - -#### `charAt` - -``` purescript -charAt :: Int -> String -> Char -``` - -Returns the character at the given index. - -**Unsafe:** throws runtime exception if the index is out of bounds. - -#### `char` - -``` purescript -char :: String -> Char -``` - -Converts a string of length `1` to a character. - -**Unsafe:** throws runtime exception if length is not `1`. - - diff --git a/package.json b/package.json index 2d38aab..55fc1c7 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,14 @@ { "private": true, "scripts": { - "postinstall": "pulp dep install", - "build": "jshint src && jscs src && pulp build && pulp test && rimraf docs && pulp docs" + "clean": "rimraf output && rimraf .pulp-cache", + "build": "jshint src && jscs src && pulp build", + "test": "jshint src && jscs src && pulp test" }, "devDependencies": { - "jscs": "^1.13.1", - "jshint": "^2.8.0", - "pulp": "^4.0.2", - "rimraf": "^2.4.1" + "jscs": "^2.8.0", + "jshint": "^2.9.1", + "pulp": "^8.1.0", + "rimraf": "^2.5.0" } } diff --git a/src/Data/String.purs b/src/Data/String.purs index aa49455..0719150 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -32,10 +32,9 @@ module Data.String ) where import Prelude -import qualified Data.Char as C + import Data.Maybe (Maybe(..), isJust) -import Data.Monoid (Monoid) -import qualified Data.String.Unsafe as U +import Data.String.Unsafe as U -- | Returns the character at the given index, if the index is within bounds. charAt :: Int -> String -> Maybe Char diff --git a/src/Data/String/Regex.purs b/src/Data/String/Regex.purs index 06539ee..717e9da 100644 --- a/src/Data/String/Regex.purs +++ b/src/Data/String/Regex.purs @@ -19,8 +19,9 @@ module Data.String.Regex ) where import Prelude -import Data.Maybe (Maybe(..)) + import Data.Either (Either(..)) +import Data.Maybe (Maybe(..)) import Data.String (contains) -- | Wraps Javascript `RegExp` objects. @@ -68,10 +69,10 @@ foreign import flags :: Regex -> RegexFlags -- | Returns the string representation of the given `RegexFlags`. renderFlags :: RegexFlags -> String renderFlags f = - (if f.global then "g" else "") ++ - (if f.ignoreCase then "i" else "") ++ - (if f.multiline then "m" else "") ++ - (if f.sticky then "y" else "") ++ + (if f.global then "g" else "") <> + (if f.ignoreCase then "i" else "") <> + (if f.multiline then "m" else "") <> + (if f.sticky then "y" else "") <> (if f.unicode then "u" else "") -- | Parses the string representation of `RegexFlags`. diff --git a/test/Test/Data/Char.purs b/test/Test/Data/Char.purs index be6b6c2..e19afa0 100644 --- a/test/Test/Data/Char.purs +++ b/test/Test/Data/Char.purs @@ -1,10 +1,15 @@ module Test.Data.Char (testChar) where -import Prelude -import Control.Monad.Eff.Console (log) +import Prelude (Unit, (==), ($), bind) + +import Control.Monad.Eff (Eff) +import Control.Monad.Eff.Console (CONSOLE, log) + import Data.Char -import Test.Assert (assert) +import Test.Assert (ASSERT, assert) + +testChar :: forall eff. Eff (console :: CONSOLE, assert :: ASSERT | eff) Unit testChar = do log "toCharCode" assert $ toCharCode 'a' == 97 diff --git a/test/Test/Data/String.purs b/test/Test/Data/String.purs index 230e264..d560a61 100644 --- a/test/Test/Data/String.purs +++ b/test/Test/Data/String.purs @@ -1,11 +1,16 @@ module Test.Data.String (testString) where -import Prelude -import Data.Maybe -import Control.Monad.Eff.Console (log) +import Prelude (Unit, Ordering(..), (==), ($), bind, negate, not, (/=), (&&)) + +import Control.Monad.Eff (Eff) +import Control.Monad.Eff.Console (CONSOLE, log) + +import Data.Maybe (Maybe(..), isNothing) import Data.String -import Test.Assert (assert) +import Test.Assert (ASSERT, assert) + +testString :: forall eff. Eff (console :: CONSOLE, assert :: ASSERT | eff) Unit testString = do log "charAt" assert $ charAt 0 "" == Nothing diff --git a/test/Test/Data/String/Regex.purs b/test/Test/Data/String/Regex.purs index db0a884..87c8313 100644 --- a/test/Test/Data/String/Regex.purs +++ b/test/Test/Data/String/Regex.purs @@ -1,17 +1,23 @@ module Test.Data.String.Regex (testStringRegex) where -import Prelude -import Data.Maybe -import Control.Monad.Eff.Console (log) +import Prelude (Unit, ($), bind, (==), not) + +import Control.Monad.Eff (Eff) +import Control.Monad.Eff.Console (CONSOLE, log) + +import Data.Either (isLeft, fromRight) +import Data.Maybe (Maybe(..)) import Data.String.Regex -import Data.Either (isLeft) -import Data.Either.Unsafe (fromRight) -import Test.Assert (assert) + +import Partial.Unsafe (unsafePartial) + +import Test.Assert (ASSERT, assert) -- | Unsafe version of `regex`. regex' :: String -> RegexFlags -> Regex -regex' pattern flags = fromRight (regex pattern flags) +regex' pattern flags = unsafePartial $ fromRight (regex pattern flags) +testStringRegex :: forall eff. Eff (console :: CONSOLE, assert :: ASSERT | eff) Unit testStringRegex = do log "regex" assert $ test (regex' "^a" noFlags) "abc" diff --git a/test/Test/Data/String/Unsafe.purs b/test/Test/Data/String/Unsafe.purs index de9a53c..10d3934 100644 --- a/test/Test/Data/String/Unsafe.purs +++ b/test/Test/Data/String/Unsafe.purs @@ -1,10 +1,15 @@ module Test.Data.String.Unsafe (testStringUnsafe) where -import Prelude -import Control.Monad.Eff.Console (log) +import Prelude (Unit, (==), ($), bind) + +import Control.Monad.Eff (Eff) +import Control.Monad.Eff.Console (CONSOLE, log) + import Data.String.Unsafe -import Test.Assert (assert) +import Test.Assert (ASSERT, assert) + +testStringUnsafe :: forall eff. Eff (console :: CONSOLE, assert :: ASSERT | eff) Unit testStringUnsafe = do log "charCodeAt" assert $ charCodeAt 0 "ab" == 97 diff --git a/test/Test/Main.purs b/test/Test/Main.purs index 78bbcf2..165098c 100644 --- a/test/Test/Main.purs +++ b/test/Test/Main.purs @@ -1,11 +1,17 @@ module Test.Main where import Prelude -import Test.Data.Char -import Test.Data.String -import Test.Data.String.Unsafe -import Test.Data.String.Regex +import Control.Monad.Eff (Eff) +import Control.Monad.Eff.Console (CONSOLE) + +import Test.Assert (ASSERT) +import Test.Data.Char (testChar) +import Test.Data.String (testString) +import Test.Data.String.Regex (testStringRegex) +import Test.Data.String.Unsafe (testStringUnsafe) + +main :: Eff (console :: CONSOLE, assert :: ASSERT) Unit main = do testChar testString From 87dd5f1694fd9dc7e052ff2b2df54e8b6b54dab5 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Sat, 21 May 2016 00:23:51 +0100 Subject: [PATCH 037/186] Update build & fix warning --- .travis.yml | 17 ++++++++--------- bower.json | 2 +- package.json | 7 ++++--- src/Data/Char.purs | 2 -- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/.travis.yml b/.travis.yml index 36183ef..3455305 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: node_js -sudo: required dist: trusty -node_js: 5 +sudo: required +node_js: 6 env: - PATH=$HOME/purescript:$PATH install: @@ -11,14 +11,13 @@ install: - chmod a+x $HOME/purescript - npm install -g bower - npm install - - bower install script: - - npm run build + - bower install --production + - npm run -s build + - bower install + - npm -s test after_success: - >- test $TRAVIS_TAG && - psc-publish > .pursuit.json && - curl -X POST http://pursuit.purescript.org/packages \ - -d @.pursuit.json \ - -H 'Accept: application/json' \ - -H "Authorization: token ${GITHUB_TOKEN}" + echo $GITHUB_TOKEN | pulp login && + echo y | pulp publish --no-push diff --git a/bower.json b/bower.json index c3445b9..6a77c06 100644 --- a/bower.json +++ b/bower.json @@ -23,6 +23,6 @@ "devDependencies": { "purescript-assert": "^1.0.0-rc.1", "purescript-console": "^1.0.0-rc.1", - "purescript-partial": "^1.1.0" + "purescript-partial": "^1.1.2" } } diff --git a/package.json b/package.json index 55fc1c7..8bbaae7 100644 --- a/package.json +++ b/package.json @@ -2,13 +2,14 @@ "private": true, "scripts": { "clean": "rimraf output && rimraf .pulp-cache", - "build": "jshint src && jscs src && pulp build", - "test": "jshint src && jscs src && pulp test" + "build": "jshint src && jscs src && pulp build --censor-lib --strict", + "test": "pulp test" }, "devDependencies": { "jscs": "^2.8.0", "jshint": "^2.9.1", - "pulp": "^8.1.0", + "pulp": "^8.2.0", + "purescript-psa": "^0.3.8", "rimraf": "^2.5.0" } } diff --git a/src/Data/Char.purs b/src/Data/Char.purs index 7c584f6..ffad899 100644 --- a/src/Data/Char.purs +++ b/src/Data/Char.purs @@ -6,8 +6,6 @@ module Data.Char , toUpper ) where -import Prelude - -- | Returns the numeric Unicode value of the character. foreign import toCharCode :: Char -> Int From c48158348af3d23ba8a467cb6b249a941f9ed2ce Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Wed, 1 Jun 2016 03:29:50 +0100 Subject: [PATCH 038/186] Update build for release --- bower.json | 8 ++++---- package.json | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bower.json b/bower.json index 6a77c06..f2dfc6d 100644 --- a/bower.json +++ b/bower.json @@ -17,12 +17,12 @@ "package.json" ], "dependencies": { - "purescript-either": "^1.0.0-rc.1", - "purescript-maybe": "^1.0.0-rc.1" + "purescript-either": "^1.0.0", + "purescript-maybe": "^1.0.0" }, "devDependencies": { - "purescript-assert": "^1.0.0-rc.1", - "purescript-console": "^1.0.0-rc.1", + "purescript-assert": "^1.0.0", + "purescript-console": "^1.0.0", "purescript-partial": "^1.1.2" } } diff --git a/package.json b/package.json index 8bbaae7..46e9a54 100644 --- a/package.json +++ b/package.json @@ -2,8 +2,8 @@ "private": true, "scripts": { "clean": "rimraf output && rimraf .pulp-cache", - "build": "jshint src && jscs src && pulp build --censor-lib --strict", - "test": "pulp test" + "build": "jshint src && jscs src && psa \"src/**/*.purs\" \"bower_components/purescript-*/src/**/*.purs\" --censor-lib --strict", + "test": "psc \"src/**/*.purs\" \"bower_components/purescript-*/src/**/*.purs\" \"test/**/*.purs\" && psc-bundle \"output/**/*.js\" --module Test.Main --main Test.Main | node" }, "devDependencies": { "jscs": "^2.8.0", From ca2fee8ee50d0e448d9840380f4d71b35fa5a98b Mon Sep 17 00:00:00 2001 From: Paul Young Date: Fri, 17 Jun 2016 21:23:24 -0700 Subject: [PATCH 039/186] Remove reference to `fromChar` in documentation - `fromChar` was removed in #59. --- src/Data/String.purs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Data/String.purs b/src/Data/String.purs index 0719150..87f41e7 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -47,7 +47,6 @@ foreign import _charAt :: (forall a. a -> Maybe a) -> Maybe Char -- | Returns a string of length `1` containing the given character. --- | Same as `fromChar`. foreign import singleton :: Char -> String -- | Returns the numeric Unicode value of the character at the given index, From dab00b716b0242063e0b9534c22b9b31eb9e5218 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Wed, 20 Jul 2016 14:16:03 +0100 Subject: [PATCH 040/186] Export `count` again --- src/Data/String.purs | 1 + test/Test/Data/String.purs | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Data/String.purs b/src/Data/String.purs index 87f41e7..1e0d6d2 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -23,6 +23,7 @@ module Data.String , dropWhile , stripPrefix , stripSuffix + , count , split , toCharArray , toLower diff --git a/test/Test/Data/String.purs b/test/Test/Data/String.purs index d560a61..4ae805e 100644 --- a/test/Test/Data/String.purs +++ b/test/Test/Data/String.purs @@ -142,6 +142,12 @@ testString = do assert $ drop 3 "ab" == "" assert $ drop (-1) "ab" == "ab" + log "count" + assert $ count (_ == 'a') "" == 0 + assert $ count (_ == 'a') "ab" == 1 + assert $ count (_ == 'a') "aaab" == 3 + assert $ count (_ == 'a') "abaa" == 1 + log "split" assert $ split "" "" == [] assert $ split "" "a" == ["a"] @@ -167,4 +173,3 @@ testString = do assert $ joinWith "" [] == "" assert $ joinWith "" ["a", "b"] == "ab" assert $ joinWith "--" ["a", "b", "c"] == "a--b--c" - From f37336c673be5d0905106a79c96eb917b5af3651 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Wed, 20 Jul 2016 14:17:41 +0100 Subject: [PATCH 041/186] Update build to use pulp again --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 46e9a54..8cdcd96 100644 --- a/package.json +++ b/package.json @@ -2,14 +2,14 @@ "private": true, "scripts": { "clean": "rimraf output && rimraf .pulp-cache", - "build": "jshint src && jscs src && psa \"src/**/*.purs\" \"bower_components/purescript-*/src/**/*.purs\" --censor-lib --strict", - "test": "psc \"src/**/*.purs\" \"bower_components/purescript-*/src/**/*.purs\" \"test/**/*.purs\" && psc-bundle \"output/**/*.js\" --module Test.Main --main Test.Main | node" + "build": "jshint src && jscs src && pulp build --censor-lib --strict", + "test": "pulp test" }, "devDependencies": { "jscs": "^2.8.0", "jshint": "^2.9.1", - "pulp": "^8.2.0", - "purescript-psa": "^0.3.8", + "pulp": "^9.0.1", + "purescript-psa": "^0.3.9", "rimraf": "^2.5.0" } } From 8f1be344654906b309a82459e47abc7e3daa8916 Mon Sep 17 00:00:00 2001 From: Risto Stevcev Date: Sat, 17 Sep 2016 15:20:03 +0200 Subject: [PATCH 042/186] Added 'splitAt' function --- src/Data/String.js | 11 +++++++++++ src/Data/String.purs | 11 +++++++++++ test/Test/Data/String.purs | 7 +++++++ 3 files changed, 29 insertions(+) diff --git a/src/Data/String.js b/src/Data/String.js index e4a44ad..eb3fb93 100644 --- a/src/Data/String.js +++ b/src/Data/String.js @@ -139,6 +139,17 @@ exports.split = function (sep) { }; }; +exports._splitAt = function (just) { + return function (nothing) { + return function (i) { + return function (s) { + return i >= 0 && i < s.length ? + just([s.substring(0, i), s.substring(i)]) : nothing; + }; + }; + }; +}; + exports.toCharArray = function (s) { return s.split(""); }; diff --git a/src/Data/String.purs b/src/Data/String.purs index 1e0d6d2..b7d8213 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -25,6 +25,7 @@ module Data.String , stripSuffix , count , split + , splitAt , toCharArray , toLower , toUpper @@ -197,6 +198,16 @@ foreign import count :: (Char -> Boolean) -> String -> Int -- | * `split " " "hello world" == ["hello", "world"]` foreign import split :: String -> String -> Array String +-- | Returns the substrings of split at the given index, if the index is within bounds. +splitAt :: Int -> String -> Maybe (Array String) +splitAt = _splitAt Just Nothing + +foreign import _splitAt :: (forall a. a -> Maybe a) + -> (forall a. Maybe a) + -> Int + -> String + -> Maybe (Array 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 4ae805e..0ca90ba 100644 --- a/test/Test/Data/String.purs +++ b/test/Test/Data/String.purs @@ -155,6 +155,13 @@ testString = do assert $ split "b" "aabcc" == ["aa", "cc"] assert $ split "d" "abc" == ["abc"] + log "splitAt" + assert $ splitAt 1 "" == Nothing + assert $ splitAt 0 "a" == Just ["", "a"] + assert $ splitAt 1 "ab" == Just ["a", "b"] + assert $ splitAt 3 "aabcc" == Just ["aab", "cc"] + assert $ splitAt (-1) "abc" == Nothing + log "toCharArray" assert $ toCharArray "" == [] assert $ toCharArray "a" == ['a'] From 3f7ad6f11a698a89e1c1ef548ec86dc318f1d897 Mon Sep 17 00:00:00 2001 From: Risto Stevcev Date: Sat, 8 Oct 2016 00:43:45 +0200 Subject: [PATCH 043/186] Added some shortcuts for commonly used flags (#67) * Added a monoid instance and helper functions to regex flags * Refactored the regex flags code --- src/Data/String/Regex.js | 2 +- src/Data/String/Regex.purs | 30 +++-------- src/Data/String/Regex/Flags.purs | 87 ++++++++++++++++++++++++++++++++ test/Test/Data/String/Regex.purs | 8 ++- 4 files changed, 103 insertions(+), 24 deletions(-) create mode 100644 src/Data/String/Regex/Flags.purs diff --git a/src/Data/String/Regex.js b/src/Data/String/Regex.js index 695303e..19e1a3d 100644 --- a/src/Data/String/Regex.js +++ b/src/Data/String/Regex.js @@ -25,7 +25,7 @@ exports.source = function (r) { return r.source; }; -exports.flags = function (r) { +exports["flags'"] = function (r) { return { multiline: r.multiline, ignoreCase: r.ignoreCase, diff --git a/src/Data/String/Regex.purs b/src/Data/String/Regex.purs index 717e9da..e665f3f 100644 --- a/src/Data/String/Regex.purs +++ b/src/Data/String/Regex.purs @@ -3,7 +3,6 @@ -- | For details of the underlying implementation, see [RegExp Reference at MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp). module Data.String.Regex ( Regex(..) - , RegexFlags(..) , regex , source , flags @@ -15,7 +14,6 @@ module Data.String.Regex , replace' , search , split - , noFlags ) where import Prelude @@ -23,6 +21,7 @@ import Prelude import Data.Either (Either(..)) import Data.Maybe (Maybe(..)) import Data.String (contains) +import Data.String.Regex.Flags (RegexFlags(..), RegexFlagsRec) -- | Wraps Javascript `RegExp` objects. foreign import data Regex :: * @@ -32,23 +31,6 @@ foreign import showRegex' :: Regex -> String instance showRegex :: Show Regex where show = showRegex' --- | Flags that control matching. -type RegexFlags = - { global :: Boolean - , ignoreCase :: Boolean - , multiline :: Boolean - , sticky :: Boolean - , unicode :: Boolean - } - --- | All flags set to false. -noFlags :: RegexFlags -noFlags = { global : false - , ignoreCase : false - , multiline : false - , sticky : false - , unicode : false } - foreign import regex' :: (String -> Either String Regex) -> (Regex -> Either String Regex) -> String @@ -64,11 +46,15 @@ regex s f = regex' Left Right s $ renderFlags f foreign import source :: Regex -> String -- | Returns the `RegexFlags` used to construct the given `Regex`. -foreign import flags :: Regex -> RegexFlags +flags :: Regex -> RegexFlags +flags = RegexFlags <<< flags' + +-- | Returns the `RegexFlags` inner record used to construct the given `Regex`. +foreign import flags' :: Regex -> RegexFlagsRec -- | Returns the string representation of the given `RegexFlags`. renderFlags :: RegexFlags -> String -renderFlags f = +renderFlags (RegexFlags f) = (if f.global then "g" else "") <> (if f.ignoreCase then "i" else "") <> (if f.multiline then "m" else "") <> @@ -77,7 +63,7 @@ renderFlags f = -- | Parses the string representation of `RegexFlags`. parseFlags :: String -> RegexFlags -parseFlags s = +parseFlags s = RegexFlags { global: contains "g" s , ignoreCase: contains "i" s , multiline: contains "m" s diff --git a/src/Data/String/Regex/Flags.purs b/src/Data/String/Regex/Flags.purs new file mode 100644 index 0000000..76e8c90 --- /dev/null +++ b/src/Data/String/Regex/Flags.purs @@ -0,0 +1,87 @@ +module Data.String.Regex.Flags where + +import Prelude (class Semigroup, (||)) +import Data.Monoid (class Monoid) + +type RegexFlagsRec = + { global :: Boolean + , ignoreCase :: Boolean + , multiline :: Boolean + , sticky :: Boolean + , unicode :: Boolean + } + +-- | Flags that control matching. +data RegexFlags = RegexFlags RegexFlagsRec + +-- | All flags set to false. +noFlags :: RegexFlags +noFlags = RegexFlags + { global: false + , ignoreCase: false + , multiline: false + , sticky: false + , unicode: false + } + +-- | Only global flag set to true +global :: RegexFlags +global = RegexFlags + { global: true + , ignoreCase: false + , multiline: false + , sticky: false + , unicode: false + } + +-- | Only ignoreCase flag set to true +ignoreCase :: RegexFlags +ignoreCase = RegexFlags + { global: false + , ignoreCase: true + , multiline: false + , sticky: false + , unicode: false + } + +-- | Only multiline flag set to true +multiline :: RegexFlags +multiline = RegexFlags + { global: false + , ignoreCase: false + , multiline: true + , sticky: false + , unicode: false + } + +-- | Only sticky flag set to true +sticky :: RegexFlags +sticky = RegexFlags + { global: false + , ignoreCase: false + , multiline: false + , sticky: true + , unicode: false + } + +-- | Only unicode flag set to true +unicode :: RegexFlags +unicode = RegexFlags + { global: false + , ignoreCase: false + , multiline: false + , sticky: false + , unicode: true + } + +instance semigroupRegexFlags :: Semigroup RegexFlags where + append (RegexFlags x) (RegexFlags y) = RegexFlags + { global: x.global || y.global + , ignoreCase: x.ignoreCase || y.ignoreCase + , multiline: x.multiline || y.multiline + , sticky: x.sticky || y.sticky + , unicode: x.unicode || y.unicode + } + +instance monoidRegexFlags :: Monoid RegexFlags where + mempty = noFlags diff --git a/test/Test/Data/String/Regex.purs b/test/Test/Data/String/Regex.purs index 87c8313..3440a6c 100644 --- a/test/Test/Data/String/Regex.purs +++ b/test/Test/Data/String/Regex.purs @@ -1,6 +1,6 @@ module Test.Data.String.Regex (testStringRegex) where -import Prelude (Unit, ($), bind, (==), not) +import Prelude (Unit, ($), (<>), bind, (==), not) import Control.Monad.Eff (Eff) import Control.Monad.Eff.Console (CONSOLE, log) @@ -8,6 +8,7 @@ import Control.Monad.Eff.Console (CONSOLE, log) import Data.Either (isLeft, fromRight) import Data.Maybe (Maybe(..)) import Data.String.Regex +import Data.String.Regex.Flags (RegexFlags, global, ignoreCase, noFlags) import Partial.Unsafe (unsafePartial) @@ -24,6 +25,11 @@ testStringRegex = do assert $ not (test (regex' "^b" noFlags) "abc") assert $ isLeft (regex "+" noFlags) + log "flags" + assert $ "quxbarfoobaz" == replace (regex' "foo" noFlags) "qux" "foobarfoobaz" + assert $ "quxbarquxbaz" == replace (regex' "foo" global) "qux" "foobarfoobaz" + assert $ "quxbarquxbaz" == replace (regex' "foo" (global <> ignoreCase)) "qux" "foobarFOObaz" + log "match" assert $ match (regex' "^abc$" noFlags) "abc" == Just [Just "abc"] From 4e5059d0478fb0e39b5a20b4c36fee6ac98ef72d Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Thu, 6 Oct 2016 00:56:42 +0100 Subject: [PATCH 044/186] Prepare for 2.0 release --- bower.json | 8 +- src/Data/Char.js | 3 - src/Data/String.js | 11 +- src/Data/String.purs | 165 ++++++++++++--------- src/Data/String/CaseInsensitiveString.purs | 22 +++ src/Data/String/Regex.js | 3 - src/Data/String/Regex.purs | 45 +++--- src/Data/String/Regex/Flags.purs | 2 +- src/Data/String/Unsafe.js | 3 - test/Test/Data/Char.purs | 1 - test/Test/Data/String.purs | 94 ++++++------ test/Test/Data/String/CaseInsensitive.purs | 18 +++ test/Test/Main.purs | 2 + 13 files changed, 227 insertions(+), 150 deletions(-) create mode 100644 src/Data/String/CaseInsensitiveString.purs create mode 100644 test/Test/Data/String/CaseInsensitive.purs diff --git a/bower.json b/bower.json index f2dfc6d..d3aa1f9 100644 --- a/bower.json +++ b/bower.json @@ -17,12 +17,12 @@ "package.json" ], "dependencies": { - "purescript-either": "^1.0.0", - "purescript-maybe": "^1.0.0" + "purescript-either": "^2.0.0", + "purescript-maybe": "^2.0.0" }, "devDependencies": { - "purescript-assert": "^1.0.0", - "purescript-console": "^1.0.0", + "purescript-assert": "^2.0.0", + "purescript-console": "^2.0.0", "purescript-partial": "^1.1.2" } } diff --git a/src/Data/Char.js b/src/Data/Char.js index 4c982b1..15f12b2 100644 --- a/src/Data/Char.js +++ b/src/Data/Char.js @@ -1,8 +1,5 @@ -/* global exports */ "use strict"; -// module Data.Char - exports.toCharCode = function (c) { return c.charCodeAt(0); }; diff --git a/src/Data/String.js b/src/Data/String.js index eb3fb93..fd42223 100644 --- a/src/Data/String.js +++ b/src/Data/String.js @@ -1,8 +1,5 @@ -/* global exports */ "use strict"; -// module Data.String - exports._charAt = function (just) { return function (nothing) { return function (i) { @@ -114,6 +111,14 @@ exports.replace = function (s1) { }; }; +exports.replaceAll = function (s1) { + return function (s2) { + return function (s3) { + return s3.replace(new RegExp(s1.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&"), "g"), s2); + }; + }; +}; + exports.take = function (n) { return function (s) { return s.substr(0, n); diff --git a/src/Data/String.purs b/src/Data/String.purs index b7d8213..849f9e8 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -2,7 +2,9 @@ -- | A String represents a sequence of characters. -- | For details of the underlying implementation, see [String Reference at MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String). module Data.String - ( charAt + ( Pattern(..) + , Replacement(..) + , charAt , charCodeAt , fromCharArray , toChar @@ -17,6 +19,7 @@ module Data.String , singleton , localeCompare , replace + , replaceAll , take , takeWhile , drop @@ -36,17 +39,39 @@ module Data.String import Prelude import Data.Maybe (Maybe(..), isJust) +import Data.Newtype (class Newtype) import Data.String.Unsafe as U +-- | A newtype used in cases where there is a string to be matched. +newtype Pattern = Pattern String + +derive instance eqPattern :: Eq Pattern +derive instance ordPattern :: Ord Pattern +derive instance newtypePattern :: Newtype Pattern _ + +instance showPattern :: Show Pattern where + show (Pattern s) = "(Pattern " <> s <> ")" + +-- | A newtype used in cases to specify a replacement for a pattern. +newtype Replacement = Replacement String + +derive instance eqReplacement :: Eq Replacement +derive instance ordReplacement :: Ord Replacement +derive instance newtypeReplacement :: Newtype Replacement _ + +instance showReplacement :: Show Replacement where + show (Replacement s) = "(Replacement " <> s <> ")" + -- | Returns the character at the given index, if the index is within bounds. charAt :: Int -> String -> Maybe Char charAt = _charAt Just Nothing -foreign import _charAt :: (forall a. a -> Maybe a) - -> (forall a. Maybe a) - -> Int - -> String - -> Maybe Char +foreign import _charAt + :: (forall a. a -> Maybe a) + -> (forall a. Maybe a) + -> Int + -> String + -> Maybe Char -- | Returns a string of length `1` containing the given character. foreign import singleton :: Char -> String @@ -56,19 +81,21 @@ foreign import singleton :: Char -> String charCodeAt :: Int -> String -> Maybe Int charCodeAt = _charCodeAt Just Nothing -foreign import _charCodeAt :: (forall a. a -> Maybe a) - -> (forall a. Maybe a) - -> Int - -> String - -> Maybe Int +foreign import _charCodeAt + :: (forall a. a -> Maybe a) + -> (forall a. Maybe a) + -> Int + -> String + -> Maybe Int toChar :: String -> Maybe Char toChar = _toChar Just Nothing -foreign import _toChar :: (forall a. a -> Maybe a) - -> (forall a. Maybe a) - -> String - -> Maybe Char +foreign import _toChar + :: (forall a. a -> Maybe a) + -> (forall a. Maybe a) + -> String + -> Maybe Char -- | Returns `true` if the given string is empty. null :: String -> Boolean @@ -81,7 +108,7 @@ uncons "" = Nothing uncons s = Just { head: U.charAt zero s, tail: drop one s } -- | Returns the longest prefix (possibly empty) of characters that satisfy --- | the predicate: +-- | the predicate. takeWhile :: (Char -> Boolean) -> String -> String takeWhile p s = take (count p s) s @@ -91,80 +118,82 @@ dropWhile p s = drop (count p s) s -- | If the string starts with the given prefix, return the portion of the -- | string left after removing it, as a Just value. Otherwise, return Nothing. --- | * `stripPrefix "http:" "http://purescript.org" == Just "//purescript.org"` --- | * `stripPrefix "http:" "https://purescript.org" == Nothing` -stripPrefix :: String -> String -> Maybe String -stripPrefix prefix str = +-- | * `stripPrefix (Pattern "http:") "http://purescript.org" == Just "//purescript.org"` +-- | * `stripPrefix (Pattern "http:") "https://purescript.org" == Nothing` +stripPrefix :: Pattern -> String -> Maybe String +stripPrefix prefix@(Pattern prefixS) str = case indexOf prefix str of - Just 0 -> Just $ drop (length prefix) str - _ -> Nothing + Just 0 -> Just $ drop (length prefixS) str + _ -> Nothing -- | If the string ends with the given suffix, return the portion of the -- | string left after removing it, as a Just value. Otherwise, return Nothing. --- | * `stripSuffix ".exe" "psc.exe" == Just "psc"` --- | * `stripSuffix ".exe" "psc" == Nothing` -stripSuffix :: String -> String -> Maybe String -stripSuffix suffix str = +-- | * `stripSuffix (Pattern ".exe") "psc.exe" == Just "psc"` +-- | * `stripSuffix (Pattern ".exe") "psc" == Nothing` +stripSuffix :: Pattern -> String -> Maybe String +stripSuffix suffix@(Pattern suffixS) str = case lastIndexOf suffix str of - Just x | x == length str - length suffix -> - Just $ take x str - _ -> - Nothing + Just x | x == length str - length suffixS -> Just $ take x str + _ -> Nothing -- | Converts an array of characters into a string. foreign import fromCharArray :: Array Char -> String -- | Checks whether the first string exists in the second string. -contains :: String -> String -> Boolean -contains x s = isJust (indexOf x s) +contains :: Pattern -> String -> Boolean +contains pat = isJust <<< indexOf pat -- | Returns the index of the first occurrence of the first string in the -- | second string. Returns `Nothing` if there is no match. -indexOf :: String -> String -> Maybe Int +indexOf :: Pattern -> String -> Maybe Int indexOf = _indexOf Just Nothing -foreign import _indexOf :: (forall a. a -> Maybe a) - -> (forall a. Maybe a) - -> String - -> String - -> Maybe Int +foreign import _indexOf + :: (forall a. a -> Maybe a) + -> (forall a. Maybe a) + -> Pattern + -> String + -> Maybe Int -- | Returns the index of the first occurrence of the first string in the -- | second string, starting at the given index. Returns `Nothing` if there is -- | no match. -indexOf' :: String -> Int -> String -> Maybe Int +indexOf' :: Pattern -> Int -> String -> Maybe Int indexOf' = _indexOf' Just Nothing -foreign import _indexOf' :: (forall a. a -> Maybe a) - -> (forall a. Maybe a) - -> String - -> Int - -> String - -> Maybe Int +foreign import _indexOf' + :: (forall a. a -> Maybe a) + -> (forall a. Maybe a) + -> Pattern + -> Int + -> String + -> Maybe Int -- | Returns the index of the last occurrence of the first string in the -- | second string. Returns `Nothing` if there is no match. -lastIndexOf :: String -> String -> Maybe Int +lastIndexOf :: Pattern -> String -> Maybe Int lastIndexOf = _lastIndexOf Just Nothing -foreign import _lastIndexOf :: (forall a. a -> Maybe a) - -> (forall a. Maybe a) - -> String - -> String - -> Maybe Int +foreign import _lastIndexOf + :: (forall a. a -> Maybe a) + -> (forall a. Maybe a) + -> Pattern + -> String + -> Maybe Int -- | Returns the index of the last occurrence of the first string in the -- | second string, starting at the given index. Returns `Nothing` if there is -- | no match. -lastIndexOf' :: String -> Int -> String -> Maybe Int +lastIndexOf' :: Pattern -> Int -> String -> Maybe Int lastIndexOf' = _lastIndexOf' Just Nothing -foreign import _lastIndexOf' :: (forall a. a -> Maybe a) - -> (forall a. Maybe a) - -> String - -> Int - -> String - -> Maybe Int +foreign import _lastIndexOf' + :: (forall a. a -> Maybe a) + -> (forall a. Maybe a) + -> Pattern + -> Int + -> String + -> Maybe Int -- | Returns the number of characters the string is composed of. foreign import length :: String -> Int @@ -173,15 +202,19 @@ foreign import length :: String -> Int localeCompare :: String -> String -> Ordering localeCompare = _localeCompare LT EQ GT -foreign import _localeCompare :: Ordering - -> Ordering - -> Ordering - -> String - -> String - -> Ordering +foreign import _localeCompare + :: Ordering + -> Ordering + -> Ordering + -> String + -> String + -> Ordering -- | Replaces the first occurence of the first argument with the second argument. -foreign import replace :: String -> String -> String -> String +foreign import replace :: Pattern -> Replacement -> String -> String + +-- | Replaces all occurences of the first argument with the second argument. +foreign import replaceAll :: Pattern -> Replacement -> String -> String -- | Returns the first `n` characters of the string. foreign import take :: Int -> String -> String @@ -196,7 +229,7 @@ foreign import count :: (Char -> Boolean) -> String -> Int -- | Returns the substrings of the second string separated along occurences -- | of the first string. -- | * `split " " "hello world" == ["hello", "world"]` -foreign import split :: String -> String -> Array String +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 (Array String) diff --git a/src/Data/String/CaseInsensitiveString.purs b/src/Data/String/CaseInsensitiveString.purs new file mode 100644 index 0000000..124810f --- /dev/null +++ b/src/Data/String/CaseInsensitiveString.purs @@ -0,0 +1,22 @@ +module Data.String.CaseInsensitive where + +import Prelude + +import Data.Newtype (class Newtype) +import Data.String (toLower) + +-- | A newtype for case insensitive string comparisons and ordering. +newtype CaseInsensitiveString = CaseInsensitiveString String + +instance eqCaseInsensitiveString :: Eq CaseInsensitiveString where + eq (CaseInsensitiveString s1) (CaseInsensitiveString s2) = + toLower s1 == toLower s2 + +instance ordCaseInsensitiveString :: Ord CaseInsensitiveString where + compare (CaseInsensitiveString s1) (CaseInsensitiveString s2) = + compare (toLower s1) (toLower s2) + +instance showCaseInsensitiveString :: Show CaseInsensitiveString where + show (CaseInsensitiveString s) = "(CaseInsensitiveString " <> s <> ")" + +derive instance newtypeCaseInsensitiveString :: Newtype CaseInsensitiveString _ diff --git a/src/Data/String/Regex.js b/src/Data/String/Regex.js index 19e1a3d..1d7e407 100644 --- a/src/Data/String/Regex.js +++ b/src/Data/String/Regex.js @@ -1,8 +1,5 @@ -/* global exports */ "use strict"; -// module Data.String.Regex - exports["showRegex'"] = function (r) { return "" + r; }; diff --git a/src/Data/String/Regex.purs b/src/Data/String/Regex.purs index e665f3f..f1e3716 100644 --- a/src/Data/String/Regex.purs +++ b/src/Data/String/Regex.purs @@ -20,7 +20,7 @@ import Prelude import Data.Either (Either(..)) import Data.Maybe (Maybe(..)) -import Data.String (contains) +import Data.String (Pattern(..), contains) import Data.String.Regex.Flags (RegexFlags(..), RegexFlagsRec) -- | Wraps Javascript `RegExp` objects. @@ -31,11 +31,12 @@ foreign import showRegex' :: Regex -> String instance showRegex :: Show Regex where show = showRegex' -foreign import regex' :: (String -> Either String Regex) - -> (Regex -> Either String Regex) - -> String - -> String - -> Either String Regex +foreign import regex' + :: (String -> Either String Regex) + -> (Regex -> Either String Regex) + -> String + -> String + -> Either String Regex -- | Constructs a `Regex` from a pattern string and flags. Fails with -- | `Left error` if the pattern contains a syntax error. @@ -64,11 +65,11 @@ renderFlags (RegexFlags f) = -- | Parses the string representation of `RegexFlags`. parseFlags :: String -> RegexFlags parseFlags s = RegexFlags - { global: contains "g" s - , ignoreCase: contains "i" s - , multiline: contains "m" s - , sticky: contains "y" s - , unicode: contains "u" s + { global: contains (Pattern "g") s + , ignoreCase: contains (Pattern "i") s + , multiline: contains (Pattern "m") s + , sticky: contains (Pattern "y") s + , unicode: contains (Pattern "u") s } -- | Returns `true` if the `Regex` matches the string. In contrast to @@ -76,11 +77,12 @@ parseFlags s = RegexFlags -- | the `lastIndex` property of the Regex. foreign import test :: Regex -> String -> Boolean -foreign import _match :: (forall r. r -> Maybe r) - -> (forall r. Maybe r) - -> Regex - -> String - -> Maybe (Array (Maybe String)) +foreign import _match + :: (forall r. r -> Maybe r) + -> (forall r. Maybe r) + -> Regex + -> String + -> Maybe (Array (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` @@ -99,11 +101,12 @@ 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 -foreign import _search :: (forall r. r -> Maybe r) - -> (forall r. Maybe r) - -> Regex - -> String - -> Maybe 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. diff --git a/src/Data/String/Regex/Flags.purs b/src/Data/String/Regex/Flags.purs index 76e8c90..045f569 100644 --- a/src/Data/String/Regex/Flags.purs +++ b/src/Data/String/Regex/Flags.purs @@ -1,6 +1,6 @@ module Data.String.Regex.Flags where -import Prelude (class Semigroup, (||)) +import Prelude import Data.Monoid (class Monoid) type RegexFlagsRec = diff --git a/src/Data/String/Unsafe.js b/src/Data/String/Unsafe.js index 726fb29..b54568f 100644 --- a/src/Data/String/Unsafe.js +++ b/src/Data/String/Unsafe.js @@ -1,8 +1,5 @@ -/* global exports */ "use strict"; -// module Data.String.Unsafe - exports.charCodeAt = function (i) { return function (s) { if (i >= 0 && i < s.length) return s.charCodeAt(i); diff --git a/test/Test/Data/Char.purs b/test/Test/Data/Char.purs index e19afa0..ad35c43 100644 --- a/test/Test/Data/Char.purs +++ b/test/Test/Data/Char.purs @@ -26,4 +26,3 @@ testChar = do log "toUpper" assert $ toUpper 'a' == 'A' assert $ toUpper 'A' == 'A' - diff --git a/test/Test/Data/String.purs b/test/Test/Data/String.purs index 0ca90ba..e706ca1 100644 --- a/test/Test/Data/String.purs +++ b/test/Test/Data/String.purs @@ -60,57 +60,57 @@ testString = do 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 + assert $ stripPrefix (Pattern "") "" == Just "" + assert $ stripPrefix (Pattern "") "abc" == Just "abc" + assert $ stripPrefix (Pattern "a") "abc" == Just "bc" + assert $ stripPrefix (Pattern "!") "abc" == Nothing + assert $ stripPrefix (Pattern "!") "" == 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") + assert $ contains (Pattern "") "" + assert $ contains (Pattern "") "abcd" + assert $ contains (Pattern "bc") "abcd" + assert $ not (contains (Pattern "cb") "abcd") log "indexOf" - assert $ indexOf "" "" == Just 0 - assert $ indexOf "" "abcd" == Just 0 - assert $ indexOf "bc" "abcd" == Just 1 - assert $ indexOf "cb" "abcd" == Nothing + assert $ indexOf (Pattern "") "" == Just 0 + assert $ indexOf (Pattern "") "abcd" == Just 0 + assert $ indexOf (Pattern "bc") "abcd" == Just 1 + assert $ indexOf (Pattern "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 + assert $ indexOf' (Pattern "") 0 "" == Just 0 + assert $ indexOf' (Pattern "") (-1) "ab" == Nothing + assert $ indexOf' (Pattern "") 0 "ab" == Just 0 + assert $ indexOf' (Pattern "") 1 "ab" == Just 1 + assert $ indexOf' (Pattern "") 2 "ab" == Just 2 + assert $ indexOf' (Pattern "") 3 "ab" == Nothing + assert $ indexOf' (Pattern "bc") 0 "abcd" == Just 1 + assert $ indexOf' (Pattern "bc") 1 "abcd" == Just 1 + assert $ indexOf' (Pattern "bc") 2 "abcd" == Nothing + assert $ indexOf' (Pattern "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 + assert $ lastIndexOf (Pattern "") "" == Just 0 + assert $ lastIndexOf (Pattern "") "abcd" == Just 4 + assert $ lastIndexOf (Pattern "bc") "abcd" == Just 1 + assert $ lastIndexOf (Pattern "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 + assert $ lastIndexOf' (Pattern "") 0 "" == Just 0 + assert $ lastIndexOf' (Pattern "") (-1) "ab" == Nothing + assert $ lastIndexOf' (Pattern "") 0 "ab" == Just 0 + assert $ lastIndexOf' (Pattern "") 1 "ab" == Just 1 + assert $ lastIndexOf' (Pattern "") 2 "ab" == Just 2 + assert $ lastIndexOf' (Pattern "") 3 "ab" == Nothing + assert $ lastIndexOf' (Pattern "bc") 0 "abcd" == Nothing + assert $ lastIndexOf' (Pattern "bc") 1 "abcd" == Just 1 + assert $ lastIndexOf' (Pattern "bc") 2 "abcd" == Just 1 + assert $ lastIndexOf' (Pattern "cb") 0 "abcd" == Nothing log "length" assert $ length "" == 0 @@ -124,9 +124,13 @@ testString = do assert $ localeCompare "b" "a" == GT log "replace" - assert $ replace "b" "" "abc" == "ac" - assert $ replace "b" "!" "abc" == "a!c" - assert $ replace "d" "!" "abc" == "abc" + assert $ replace (Pattern "b") (Replacement "") "abc" == "ac" + assert $ replace (Pattern "b") (Replacement "!") "abc" == "a!c" + assert $ replace (Pattern "d") (Replacement "!") "abc" == "abc" + + log "replaceAll" + assert $ replaceAll (Pattern "b") (Replacement "") "abbbbbc" == "ac" + assert $ replaceAll (Pattern "[b]") (Replacement "!") "a[b]c" == "a!c" log "take" assert $ take 0 "ab" == "" @@ -149,11 +153,11 @@ testString = do assert $ count (_ == 'a') "abaa" == 1 log "split" - assert $ split "" "" == [] - assert $ split "" "a" == ["a"] - assert $ split "" "ab" == ["a", "b"] - assert $ split "b" "aabcc" == ["aa", "cc"] - assert $ split "d" "abc" == ["abc"] + assert $ split (Pattern "") "" == [] + assert $ split (Pattern "") "a" == ["a"] + assert $ split (Pattern "") "ab" == ["a", "b"] + assert $ split (Pattern "b") "aabcc" == ["aa", "cc"] + assert $ split (Pattern "d") "abc" == ["abc"] log "splitAt" assert $ splitAt 1 "" == Nothing diff --git a/test/Test/Data/String/CaseInsensitive.purs b/test/Test/Data/String/CaseInsensitive.purs new file mode 100644 index 0000000..e47b4a9 --- /dev/null +++ b/test/Test/Data/String/CaseInsensitive.purs @@ -0,0 +1,18 @@ +module Test.Data.String.CaseInsensitive (testCaseInsensitiveString) where + +import Prelude (Unit, (==), ($), bind, compare, Ordering(..)) + +import Control.Monad.Eff (Eff) +import Control.Monad.Eff.Console (CONSOLE, log) + +import Data.String.CaseInsensitive + +import Test.Assert (ASSERT, assert) + +testCaseInsensitiveString :: forall eff. Eff (console :: CONSOLE, assert :: ASSERT | eff) Unit +testCaseInsensitiveString = do + log "equality" + assert $ CaseInsensitiveString "aB" == CaseInsensitiveString "AB" + + log "comparison" + assert $ compare (CaseInsensitiveString "qwerty") (CaseInsensitiveString "QWERTY") == EQ diff --git a/test/Test/Main.purs b/test/Test/Main.purs index 165098c..b2c7f50 100644 --- a/test/Test/Main.purs +++ b/test/Test/Main.purs @@ -10,6 +10,7 @@ import Test.Data.Char (testChar) import Test.Data.String (testString) import Test.Data.String.Regex (testStringRegex) import Test.Data.String.Unsafe (testStringUnsafe) +import Test.Data.String.CaseInsensitive (testCaseInsensitiveString) main :: Eff (console :: CONSOLE, assert :: ASSERT) Unit main = do @@ -17,3 +18,4 @@ main = do testString testStringUnsafe testStringRegex + testCaseInsensitiveString From 96b451ac69dabbbb7f52a3fb84576744465669e9 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Fri, 7 Oct 2016 23:58:44 +0100 Subject: [PATCH 045/186] Add some extra instances for RegexFlags --- src/Data/String/Regex/Flags.purs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/Data/String/Regex/Flags.purs b/src/Data/String/Regex/Flags.purs index 045f569..2684498 100644 --- a/src/Data/String/Regex/Flags.purs +++ b/src/Data/String/Regex/Flags.purs @@ -1,7 +1,11 @@ module Data.String.Regex.Flags where import Prelude + +import Control.MonadPlus (guard) + import Data.Monoid (class Monoid) +import Data.String (joinWith) type RegexFlagsRec = { global :: Boolean @@ -85,3 +89,26 @@ instance semigroupRegexFlags :: Semigroup RegexFlags where instance monoidRegexFlags :: Monoid RegexFlags where mempty = noFlags + +instance eqRegexFlags :: Eq RegexFlags where + eq (RegexFlags x) (RegexFlags y) + = x.global == y.global + && x.ignoreCase == y.ignoreCase + && x.multiline == y.multiline + && x.sticky == y.sticky + && x.unicode == y.unicode + +instance showRegexFlags :: Show RegexFlags where + show (RegexFlags flags) = + let + usedFlags = + [] + <> (guard flags.global $> "global") + <> (guard flags.ignoreCase $> "ignoreCase") + <> (guard flags.multiline $> "multiline") + <> (guard flags.sticky $> "sticky") + <> (guard flags.unicode $> "unicode") + in + if usedFlags == [] + then "noFlags" + else "(" <> joinWith " <> " usedFlags <> ")" From c04a774a89e5ca4ea77cf73934d5eca96e9c9ab7 Mon Sep 17 00:00:00 2001 From: Risto Stevcev Date: Sat, 8 Oct 2016 02:03:32 +0200 Subject: [PATCH 046/186] Rewrote null check to use mempty --- src/Data/String.purs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Data/String.purs b/src/Data/String.purs index b7d8213..3ff773b 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -36,6 +36,7 @@ module Data.String import Prelude import Data.Maybe (Maybe(..), isJust) +import Data.Monoid (mempty) import Data.String.Unsafe as U -- | Returns the character at the given index, if the index is within bounds. @@ -72,7 +73,7 @@ foreign import _toChar :: (forall a. a -> Maybe a) -- | Returns `true` if the given string is empty. null :: String -> Boolean -null s = length s == zero +null = eq mempty -- | Returns the first character and the rest of the string, -- | if the string is not empty. From ab78019ef5624d4cbc832a532b509c1c2df86e58 Mon Sep 17 00:00:00 2001 From: Risto Stevcev Date: Sat, 8 Oct 2016 13:43:01 +0200 Subject: [PATCH 047/186] Rewrote null to check the empty string --- src/Data/String.purs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Data/String.purs b/src/Data/String.purs index 3ff773b..4a56cb2 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -36,7 +36,6 @@ module Data.String import Prelude import Data.Maybe (Maybe(..), isJust) -import Data.Monoid (mempty) import Data.String.Unsafe as U -- | Returns the character at the given index, if the index is within bounds. @@ -73,7 +72,7 @@ foreign import _toChar :: (forall a. a -> Maybe a) -- | Returns `true` if the given string is empty. null :: String -> Boolean -null = eq mempty +null s = s == "" -- | Returns the first character and the rest of the string, -- | if the string is not empty. From 332077b01204cd489345a38b11bf29db9a93d178 Mon Sep 17 00:00:00 2001 From: Jack Leigh Date: Wed, 26 Oct 2016 12:33:09 +0100 Subject: [PATCH 048/186] Update documentation - split now takes a Pattern --- src/Data/String.purs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Data/String.purs b/src/Data/String.purs index 6f2f215..9eb2f6c 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -228,7 +228,7 @@ foreign import count :: (Char -> Boolean) -> String -> Int -- | Returns the substrings of the second string separated along occurences -- | of the first string. --- | * `split " " "hello world" == ["hello", "world"]` +-- | * `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. From 94d6526ca7c692cc6917225f68a7bcabeebf9555 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Sat, 19 Nov 2016 15:28:28 +0000 Subject: [PATCH 049/186] Update README.md --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1cb010e..0565699 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@ # purescript-strings -[![Latest release](http://img.shields.io/bower/v/purescript-strings.svg)](https://github.com/purescript/purescript-strings/releases) -[![Build Status](https://travis-ci.org/purescript/purescript-strings.svg?branch=master)](https://travis-ci.org/purescript/purescript-strings) -[![Dependency Status](https://www.versioneye.com/user/projects/55848c29363861001b000193/badge.svg?style=flat)](https://www.versioneye.com/user/projects/55848c29363861001b000193) +[![Latest release](http://img.shields.io/github/release/purescript/purescript-strings.svg)](https://github.com/purescript/purescript-strings/releases) +[![Build status](https://travis-ci.org/purescript/purescript-strings.svg?branch=master)](https://travis-ci.org/purescript/purescript-strings) String and char utility functions, regular expressions. From f7f449cfb8ac60827f3e42809a3f39391e444d29 Mon Sep 17 00:00:00 2001 From: Harry Garrood Date: Thu, 1 Dec 2016 23:01:48 +0000 Subject: [PATCH 050/186] Make splitAt return a record, fixes #69 --- src/Data/String.js | 3 ++- src/Data/String.purs | 4 ++-- test/Test/Data/String.purs | 20 ++++++++++++++------ 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/Data/String.js b/src/Data/String.js index fd42223..7b21753 100644 --- a/src/Data/String.js +++ b/src/Data/String.js @@ -149,7 +149,8 @@ exports._splitAt = function (just) { return function (i) { return function (s) { return i >= 0 && i < s.length ? - just([s.substring(0, i), s.substring(i)]) : nothing; + just({ before: s.substring(0, i), after: s.substring(i) }) : + nothing; }; }; }; diff --git a/src/Data/String.purs b/src/Data/String.purs index 9eb2f6c..22be682 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -232,14 +232,14 @@ foreign import count :: (Char -> Boolean) -> String -> Int 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 (Array String) +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 (Array String) + -> Maybe { 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 e706ca1..541bb1b 100644 --- a/test/Test/Data/String.purs +++ b/test/Test/Data/String.purs @@ -5,7 +5,7 @@ import Prelude (Unit, Ordering(..), (==), ($), bind, negate, not, (/=), (&&)) import Control.Monad.Eff (Eff) import Control.Monad.Eff.Console (CONSOLE, log) -import Data.Maybe (Maybe(..), isNothing) +import Data.Maybe (Maybe(..), isNothing, maybe) import Data.String import Test.Assert (ASSERT, assert) @@ -160,11 +160,19 @@ testString = do assert $ split (Pattern "d") "abc" == ["abc"] log "splitAt" - assert $ splitAt 1 "" == Nothing - assert $ splitAt 0 "a" == Just ["", "a"] - assert $ splitAt 1 "ab" == Just ["a", "b"] - assert $ splitAt 3 "aabcc" == Just ["aab", "cc"] - assert $ splitAt (-1) "abc" == Nothing + 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 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 log "toCharArray" assert $ toCharArray "" == [] From 1108a606b822ccafa12babeb219ae7d42862a367 Mon Sep 17 00:00:00 2001 From: rightfold Date: Sun, 25 Dec 2016 04:37:48 +0100 Subject: [PATCH 051/186] Add unsafeRegex (#74) * Add unsafeRegex Fixes #73. * Make purescript-partial a non-dev dependency * Move unsafeRegex to Unsafe module --- bower.json | 6 ++--- src/Data/String/Regex/Unsafe.purs | 14 +++++++++++ test/Test/Data/String/Regex.purs | 41 ++++++++++++++----------------- 3 files changed, 35 insertions(+), 26 deletions(-) create mode 100644 src/Data/String/Regex/Unsafe.purs diff --git a/bower.json b/bower.json index d3aa1f9..1ac48f3 100644 --- a/bower.json +++ b/bower.json @@ -18,11 +18,11 @@ ], "dependencies": { "purescript-either": "^2.0.0", - "purescript-maybe": "^2.0.0" + "purescript-maybe": "^2.0.0", + "purescript-partial": "^1.1.2" }, "devDependencies": { "purescript-assert": "^2.0.0", - "purescript-console": "^2.0.0", - "purescript-partial": "^1.1.2" + "purescript-console": "^2.0.0" } } diff --git a/src/Data/String/Regex/Unsafe.purs b/src/Data/String/Regex/Unsafe.purs new file mode 100644 index 0000000..0a982c6 --- /dev/null +++ b/src/Data/String/Regex/Unsafe.purs @@ -0,0 +1,14 @@ +module Data.String.Regex.Unsafe +( unsafeRegex +) where + +import Data.Either (fromRight) +import Data.String.Regex (Regex, regex) +import Data.String.Regex.Flags (RegexFlags) + +import Partial.Unsafe (unsafePartial) + +-- | Constructs a `Regex` from a pattern string and flags. Fails with +-- | an exception if the pattern contains a syntax error. +unsafeRegex :: String -> RegexFlags -> Regex +unsafeRegex s f = unsafePartial fromRight (regex s f) diff --git a/test/Test/Data/String/Regex.purs b/test/Test/Data/String/Regex.purs index 3440a6c..8b9fc3f 100644 --- a/test/Test/Data/String/Regex.purs +++ b/test/Test/Data/String/Regex.purs @@ -5,53 +5,48 @@ import Prelude (Unit, ($), (<>), bind, (==), not) import Control.Monad.Eff (Eff) import Control.Monad.Eff.Console (CONSOLE, log) -import Data.Either (isLeft, fromRight) +import Data.Either (isLeft) import Data.Maybe (Maybe(..)) import Data.String.Regex -import Data.String.Regex.Flags (RegexFlags, global, ignoreCase, noFlags) - -import Partial.Unsafe (unsafePartial) +import Data.String.Regex.Flags (global, ignoreCase, noFlags) +import Data.String.Regex.Unsafe (unsafeRegex) import Test.Assert (ASSERT, assert) --- | Unsafe version of `regex`. -regex' :: String -> RegexFlags -> Regex -regex' pattern flags = unsafePartial $ fromRight (regex pattern flags) - testStringRegex :: forall eff. Eff (console :: CONSOLE, assert :: ASSERT | eff) Unit testStringRegex = do log "regex" - assert $ test (regex' "^a" noFlags) "abc" - assert $ not (test (regex' "^b" noFlags) "abc") + assert $ test (unsafeRegex "^a" noFlags) "abc" + assert $ not (test (unsafeRegex "^b" noFlags) "abc") assert $ isLeft (regex "+" noFlags) log "flags" - assert $ "quxbarfoobaz" == replace (regex' "foo" noFlags) "qux" "foobarfoobaz" - assert $ "quxbarquxbaz" == replace (regex' "foo" global) "qux" "foobarfoobaz" - assert $ "quxbarquxbaz" == replace (regex' "foo" (global <> ignoreCase)) "qux" "foobarFOObaz" + assert $ "quxbarfoobaz" == replace (unsafeRegex "foo" noFlags) "qux" "foobarfoobaz" + assert $ "quxbarquxbaz" == replace (unsafeRegex "foo" global) "qux" "foobarfoobaz" + assert $ "quxbarquxbaz" == replace (unsafeRegex "foo" (global <> ignoreCase)) "qux" "foobarFOObaz" log "match" - assert $ match (regex' "^abc$" noFlags) "abc" == Just [Just "abc"] + assert $ match (unsafeRegex "^abc$" noFlags) "abc" == Just [Just "abc"] log "replace" - assert $ replace (regex' "-" noFlags) "!" "a-b-c" == "a!b-c" + assert $ replace (unsafeRegex "-" noFlags) "!" "a-b-c" == "a!b-c" log "replace'" - assert $ replace' (regex' "-" noFlags) (\s xs -> "!") "a-b-c" == "a!b-c" + assert $ replace' (unsafeRegex "-" 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 + assert $ search (unsafeRegex "b" noFlags) "abc" == Just 1 + assert $ search (unsafeRegex "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"] + assert $ split (unsafeRegex "" noFlags) "" == [] + assert $ split (unsafeRegex "" noFlags) "abc" == ["a", "b", "c"] + assert $ split (unsafeRegex "b" noFlags) "" == [""] + assert $ split (unsafeRegex "b" noFlags) "abc" == ["a", "c"] log "test" -- Ensure that we have referential transparency for calls to 'test'. No -- global state should be maintained between these two calls: - let pattern = regex' "a" (parseFlags "g") + let pattern = unsafeRegex "a" (parseFlags "g") assert $ test pattern "a" assert $ test pattern "a" From ed00f298cff06cc066f3593cdf6b0107692aa1dd Mon Sep 17 00:00:00 2001 From: Phil Freeman Date: Sat, 24 Dec 2016 19:40:20 -0800 Subject: [PATCH 052/186] v2.1.0 From a1c83a75828688529b3c9139f59c235b305bbb17 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Sun, 12 Mar 2017 15:11:56 +0000 Subject: [PATCH 053/186] Update for PureScript 0.11 --- .eslintrc.json | 28 ++++++++++++++++++++++ .gitignore | 3 +-- .jscsrc | 17 ------------- .jshintrc | 20 ---------------- .travis.yml | 2 +- bower.json | 10 ++++---- package.json | 11 ++++----- src/Data/String.js | 3 ++- src/Data/String/Regex.purs | 2 +- test/Test/Data/Char.purs | 2 +- test/Test/Data/String.purs | 2 +- test/Test/Data/String/CaseInsensitive.purs | 2 +- test/Test/Data/String/Regex.purs | 2 +- test/Test/Data/String/Unsafe.purs | 2 +- 14 files changed, 48 insertions(+), 58 deletions(-) create mode 100644 .eslintrc.json delete mode 100644 .jscsrc delete mode 100644 .jshintrc diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..84cef4f --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,28 @@ +{ + "parserOptions": { + "ecmaVersion": 5 + }, + "extends": "eslint:recommended", + "env": { + "commonjs": true + }, + "rules": { + "strict": [2, "global"], + "block-scoped-var": 2, + "consistent-return": 2, + "eqeqeq": [2, "smart"], + "guard-for-in": 2, + "no-caller": 2, + "no-extend-native": 2, + "no-loop-func": 2, + "no-new": 2, + "no-param-reassign": 2, + "no-return-assign": 2, + "no-unused-expressions": 2, + "no-use-before-define": 2, + "radix": [2, "always"], + "indent": [2, 2], + "quotes": [2, "double"], + "semi": [2, "always"] + } +} diff --git a/.gitignore b/.gitignore index e306283..7050558 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ /.* !/.gitignore -!/.jscsrc -!/.jshintrc +!/.eslintrc.json !/.travis.yml /bower_components/ /node_modules/ diff --git a/.jscsrc b/.jscsrc deleted file mode 100644 index 2561ce9..0000000 --- a/.jscsrc +++ /dev/null @@ -1,17 +0,0 @@ -{ - "preset": "grunt", - "disallowSpacesInFunctionExpression": null, - "requireSpacesInFunctionExpression": { - "beforeOpeningRoundBrace": true, - "beforeOpeningCurlyBrace": true - }, - "disallowSpacesInAnonymousFunctionExpression": null, - "requireSpacesInAnonymousFunctionExpression": { - "beforeOpeningRoundBrace": true, - "beforeOpeningCurlyBrace": true - }, - "disallowSpacesInsideObjectBrackets": null, - "requireSpacesInsideObjectBrackets": "all", - "validateQuoteMarks": "\"", - "requireCurlyBraces": null -} diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index 620d8d7..0000000 --- a/.jshintrc +++ /dev/null @@ -1,20 +0,0 @@ -{ - "bitwise": true, - "eqeqeq": true, - "forin": true, - "freeze": true, - "funcscope": true, - "futurehostile": true, - "strict": "global", - "latedef": true, - "maxparams": 1, - "noarg": true, - "nocomma": true, - "nonew": true, - "notypeof": true, - "singleGroups": true, - "undef": true, - "unused": true, - "eqnull": true, - "predef": ["exports"] -} diff --git a/.travis.yml b/.travis.yml index 3455305..4cbd5fd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: node_js dist: trusty sudo: required -node_js: 6 +node_js: stable env: - PATH=$HOME/purescript:$PATH install: diff --git a/bower.json b/bower.json index 1ac48f3..10f0105 100644 --- a/bower.json +++ b/bower.json @@ -17,12 +17,12 @@ "package.json" ], "dependencies": { - "purescript-either": "^2.0.0", - "purescript-maybe": "^2.0.0", - "purescript-partial": "^1.1.2" + "purescript-either": "^3.0.0", + "purescript-maybe": "^3.0.0", + "purescript-partial": "^1.2.0" }, "devDependencies": { - "purescript-assert": "^2.0.0", - "purescript-console": "^2.0.0" + "purescript-assert": "^3.0.0", + "purescript-console": "^3.0.0" } } diff --git a/package.json b/package.json index 8cdcd96..132cefc 100644 --- a/package.json +++ b/package.json @@ -2,14 +2,13 @@ "private": true, "scripts": { "clean": "rimraf output && rimraf .pulp-cache", - "build": "jshint src && jscs src && pulp build --censor-lib --strict", + "build": "eslint src && pulp build -- --censor-lib --strict", "test": "pulp test" }, "devDependencies": { - "jscs": "^2.8.0", - "jshint": "^2.9.1", - "pulp": "^9.0.1", - "purescript-psa": "^0.3.9", - "rimraf": "^2.5.0" + "eslint": "^3.17.1", + "pulp": "^10.0.4", + "purescript-psa": "^0.5.0-rc.1", + "rimraf": "^2.6.1" } } diff --git a/src/Data/String.js b/src/Data/String.js index fd42223..634d91a 100644 --- a/src/Data/String.js +++ b/src/Data/String.js @@ -133,7 +133,8 @@ exports.drop = function (n) { exports.count = function (p) { return function (s) { - for (var i = 0; i < s.length && p(s.charAt(i)); i++); {} + var i = 0; + while (i < s.length && p(s.charAt(i))) i++; return i; }; }; diff --git a/src/Data/String/Regex.purs b/src/Data/String/Regex.purs index f1e3716..d60b9ee 100644 --- a/src/Data/String/Regex.purs +++ b/src/Data/String/Regex.purs @@ -24,7 +24,7 @@ import Data.String (Pattern(..), contains) import Data.String.Regex.Flags (RegexFlags(..), RegexFlagsRec) -- | Wraps Javascript `RegExp` objects. -foreign import data Regex :: * +foreign import data Regex :: Type foreign import showRegex' :: Regex -> String diff --git a/test/Test/Data/Char.purs b/test/Test/Data/Char.purs index ad35c43..ee86573 100644 --- a/test/Test/Data/Char.purs +++ b/test/Test/Data/Char.purs @@ -1,6 +1,6 @@ module Test.Data.Char (testChar) where -import Prelude (Unit, (==), ($), bind) +import Prelude (Unit, (==), ($), discard) import Control.Monad.Eff (Eff) import Control.Monad.Eff.Console (CONSOLE, log) diff --git a/test/Test/Data/String.purs b/test/Test/Data/String.purs index e706ca1..bc58ee1 100644 --- a/test/Test/Data/String.purs +++ b/test/Test/Data/String.purs @@ -1,6 +1,6 @@ module Test.Data.String (testString) where -import Prelude (Unit, Ordering(..), (==), ($), bind, negate, not, (/=), (&&)) +import Prelude (Unit, Ordering(..), (==), ($), discard, negate, not, (/=), (&&)) import Control.Monad.Eff (Eff) import Control.Monad.Eff.Console (CONSOLE, log) diff --git a/test/Test/Data/String/CaseInsensitive.purs b/test/Test/Data/String/CaseInsensitive.purs index e47b4a9..9bd4530 100644 --- a/test/Test/Data/String/CaseInsensitive.purs +++ b/test/Test/Data/String/CaseInsensitive.purs @@ -1,6 +1,6 @@ module Test.Data.String.CaseInsensitive (testCaseInsensitiveString) where -import Prelude (Unit, (==), ($), bind, compare, Ordering(..)) +import Prelude (Unit, (==), ($), discard, compare, Ordering(..)) import Control.Monad.Eff (Eff) import Control.Monad.Eff.Console (CONSOLE, log) diff --git a/test/Test/Data/String/Regex.purs b/test/Test/Data/String/Regex.purs index 8b9fc3f..6228b91 100644 --- a/test/Test/Data/String/Regex.purs +++ b/test/Test/Data/String/Regex.purs @@ -1,6 +1,6 @@ module Test.Data.String.Regex (testStringRegex) where -import Prelude (Unit, ($), (<>), bind, (==), not) +import Prelude (Unit, ($), (<>), discard, (==), not) import Control.Monad.Eff (Eff) import Control.Monad.Eff.Console (CONSOLE, log) diff --git a/test/Test/Data/String/Unsafe.purs b/test/Test/Data/String/Unsafe.purs index 10d3934..bd2924d 100644 --- a/test/Test/Data/String/Unsafe.purs +++ b/test/Test/Data/String/Unsafe.purs @@ -1,6 +1,6 @@ module Test.Data.String.Unsafe (testStringUnsafe) where -import Prelude (Unit, (==), ($), bind) +import Prelude (Unit, (==), ($), discard) import Control.Monad.Eff (Eff) import Control.Monad.Eff.Console (CONSOLE, log) From f4759c68e4fd2a7f2814b71b58d3cbe46781f56c Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Thu, 27 Apr 2017 23:39:14 +0100 Subject: [PATCH 054/186] Add some generator functions --- bower.json | 1 + src/Data/Char/Gen.purs | 22 ++++++++++++++++++++++ src/Data/String/Gen.purs | 29 +++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+) create mode 100644 src/Data/Char/Gen.purs create mode 100644 src/Data/String/Gen.purs diff --git a/bower.json b/bower.json index 10f0105..e92fb95 100644 --- a/bower.json +++ b/bower.json @@ -18,6 +18,7 @@ ], "dependencies": { "purescript-either": "^3.0.0", + "purescript-gen": "^1.1.0", "purescript-maybe": "^3.0.0", "purescript-partial": "^1.2.0" }, diff --git a/src/Data/Char/Gen.purs b/src/Data/Char/Gen.purs new file mode 100644 index 0000000..cab95a0 --- /dev/null +++ b/src/Data/Char/Gen.purs @@ -0,0 +1,22 @@ +module Data.Char.Gen where + +import Prelude + +import Control.Monad.Gen (class MonadGen, chooseInt) +import Data.Char as C + +-- | Generates a character of the Unicode basic multilingual plain. +genUnicodeChar :: forall m. MonadGen m => m Char +genUnicodeChar = C.fromCharCode <$> chooseInt 0 65536 + +-- | Generates a character in the ASCII character set, excluding control codes. +genAsciiChar :: forall m. MonadGen m => m Char +genAsciiChar = C.fromCharCode <$> chooseInt 32 127 + +-- | Generates a character in the ASCII character set. +genAsciiChar' :: forall m. MonadGen m => m Char +genAsciiChar' = C.fromCharCode <$> chooseInt 0 127 + +-- | Generates a character that is a numeric digit. +genDigitChar :: forall m. MonadGen m => m Char +genDigitChar = C.fromCharCode <$> chooseInt 48 57 diff --git a/src/Data/String/Gen.purs b/src/Data/String/Gen.purs new file mode 100644 index 0000000..0521711 --- /dev/null +++ b/src/Data/String/Gen.purs @@ -0,0 +1,29 @@ +module Data.String.Gen where + +import Prelude + +import Control.Monad.Gen (class MonadGen, unfoldable) +import Control.Monad.Rec.Class (class MonadRec) +import Data.Char.Gen as CG +import Data.String as S + +-- | Generates a string using the specified character generator. +genString :: forall m. MonadRec m => MonadGen m => m Char -> m String +genString = map S.fromCharArray <<< unfoldable + +-- | Generates a string using characters from the Unicode basic multilingual +-- | plain. +genUnicodeString :: forall m. MonadRec m => MonadGen m => m String +genUnicodeString = genString CG.genUnicodeChar + +-- | Generates a string using the ASCII character set, excluding control codes. +genAsciiString :: forall m. MonadRec m => MonadGen m => m String +genAsciiString = genString CG.genAsciiChar + +-- | Generates a string using the ASCII character set. +genAsciiString' :: forall m. MonadRec m => MonadGen m => m String +genAsciiString' = genString CG.genAsciiChar' + +-- | Generates a string made up of numeric digits. +genDigitString :: forall m. MonadRec m => MonadGen m => m String +genDigitString = genString CG.genDigitChar From 428b99547ca167f2b93b69c67b0cc81a71128b3d Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Wed, 24 May 2017 23:29:17 -0600 Subject: [PATCH 055/186] WIP code point based string functions --- src/Data/String/CodePoints.js | 71 +++++++++++++++++++++++++++++++++ src/Data/String/CodePoints.purs | 66 ++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 src/Data/String/CodePoints.js create mode 100644 src/Data/String/CodePoints.purs diff --git a/src/Data/String/CodePoints.js b/src/Data/String/CodePoints.js new file mode 100644 index 0000000..e4e56cf --- /dev/null +++ b/src/Data/String/CodePoints.js @@ -0,0 +1,71 @@ +const hasArrayFrom = typeof Array.from === 'function'; +const hasStringIterator = + typeof Symbol !== 'undefined' && + Symbol != null && + typeof Symbol.iterator !== 'undefined' && + typeof String.prototype[Symbol.iterator] === 'function'; + +exports._codePointAt = function (fallback) { + return function (Just) { + return function (Nothing) { + return function (relIndex) { + return function (str) { + let length = str.length; + if (length <= relIndex) return Nothing; + let index = relIndex < 0 ? ((relIndex % length) + length) % length : relIndex; + if (typeof String.prototype.codePointAt === 'function') { + let cp = str.codePointAt(index); + return cp == null ? Nothing : Just(cp); + } else if (hasArrayFrom) { + let cps = Array.from(str); + if (cps.length <= index) return Nothing; + return Just(cps[index]); + } else if (hasStringIterator) { + let iter = str[Symbol.iterator](); + for (;;) { + let { value, done } = iter.next(); + if (done) return Nothing; + if (i == 0) return Just(value); + --i; + } + } + return fallback(index)(str); + }; + }; + }; + }; +}; + +exports._toCodePointArray = function (str) { + if (hasArrayFrom) { + return Array.from(str); + } else if (hasStringIterator) { + let accum = []; + let iter = str[Symbol.iterator](); + for (;;) { + let { value, done } = iter.next(); + if (done) return accum; + accum.push(value); + } + } + let accum = []; + for (let cuCount = 0; cuCount < str.length; ++cuCount) { + let cu = str[cuCount]; + let cp = cu; + if (isLead(cu) && cuCount + 1 < str.length) { + let lead = cu; + let trail = str[cuCount + 1]; + if (isTrail(trail)) { + cp = unsurrogate(lead, trail); + } + } + accum.push(cp); + } + return accum; +}; + +function isLead(cu) { return 0xD800 <= cu && cu <= 0xDBFF; } +function isTrail(cu) { return 0xDC00 <= cu && cu <= 0xDFFF; } +function unsurrogate(h, l) { + return (h - 0xD800) * 0x400 + (l - 0xDC00) + 0x10000; +} diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs new file mode 100644 index 0000000..7176282 --- /dev/null +++ b/src/Data/String/CodePoints.purs @@ -0,0 +1,66 @@ +module CodePoints + ( CodePoint() + --, Pattern() + --, codePointAt + --, fromCodePointArray + --, contains + --, indexOf + --, indexOf' + --, lastIndexOf + --, lastIndexOf' + --, uncons + --, length + --, singleton + --, replace + --, replaceAll + --, take + --, takeWhile + --, drop + --, dropWhile + --, stripPrefix + --, stripSuffix + --, count + --, split + --, splitAt + --, toCodePointArray + ) where + +import Prelude ((&&), (<=), (*), (+), (-)) +import Data.Maybe (Maybe(Just, Nothing)) + +newtype CodePoint = CodePoint Int + +codePointFromInt :: Int -> Maybe CodePoint +codePointFromInt n | 0 <= n && n <= 0x10FFFF = Just (CodePoint n) +codePointFromInt n = Nothing + +codePointToInt :: CodePoint -> Int +codePointToInt (CodePoint n) = n + +codePointFromSurrogatePair :: Int -> Int -> Maybe CodePoint +codePointFromSurrogatePair lead trail | isLead lead && isTrail trail + = Just (CodePoint (unsurrogate lead trail)) + where unsurrogate h l = (h - 0xD800) * 0x400 + (l - 0xDC00) + 0x10000 +codePointFromSurrogatePair _ _ = Nothing + +isLead :: Int -> Boolean +isLead cu = 0xD800 <= cu && cu <= 0xDBFF + +isTrail :: Int -> Boolean +isTrail cu = 0xDC00 <= cu && cu <= 0xDFFF + +codePointAt :: Int -> String -> Maybe CodePoint +codePointAt = _codePointAt (Just . CodePoint) Nothing + +foreign import _codePointAt + :: (Int -> String -> Maybe CodePoint) + -> (forall a. a -> Maybe a) + -> (forall a. Maybe a) + -> Int + -> String + -> Maybe CodePoint + +codePointAtFallback :: Int -> String -> Maybe CodePoint +codePointAtFallback n s = CodePoint <$> index (toCodePointArray s) n + +foreign import _toCodePointArray :: String -> Array CodePoint From dc0577c026065b722ea86c45b22ffaf59f4016c4 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Thu, 25 May 2017 00:16:56 -0600 Subject: [PATCH 056/186] more progress --- bower.json | 5 ++- src/Data/String/CodePoints.js | 63 ++++++++++++--------------------- src/Data/String/CodePoints.purs | 37 +++++++++++++++---- 3 files changed, 57 insertions(+), 48 deletions(-) diff --git a/bower.json b/bower.json index e92fb95..a1fa92d 100644 --- a/bower.json +++ b/bower.json @@ -20,7 +20,10 @@ "purescript-either": "^3.0.0", "purescript-gen": "^1.1.0", "purescript-maybe": "^3.0.0", - "purescript-partial": "^1.2.0" + "purescript-partial": "^1.2.0", + "purescript-unfoldable": "^3.0.0", + "purescript-lists": "^4.1.1", + "purescript-arrays": "^4.0.1" }, "devDependencies": { "purescript-assert": "^3.0.0", diff --git a/src/Data/String/CodePoints.js b/src/Data/String/CodePoints.js index e4e56cf..16d499d 100644 --- a/src/Data/String/CodePoints.js +++ b/src/Data/String/CodePoints.js @@ -1,5 +1,5 @@ -const hasArrayFrom = typeof Array.from === 'function'; -const hasStringIterator = +var hasArrayFrom = typeof Array.from === 'function'; +var hasStringIterator = typeof Symbol !== 'undefined' && Symbol != null && typeof Symbol.iterator !== 'undefined' && @@ -10,22 +10,22 @@ exports._codePointAt = function (fallback) { return function (Nothing) { return function (relIndex) { return function (str) { - let length = str.length; + var length = str.length; if (length <= relIndex) return Nothing; - let index = relIndex < 0 ? ((relIndex % length) + length) % length : relIndex; + var index = relIndex < 0 ? ((relIndex % length) + length) % length : relIndex; if (typeof String.prototype.codePointAt === 'function') { - let cp = str.codePointAt(index); + var cp = str.codePointAt(index); return cp == null ? Nothing : Just(cp); } else if (hasArrayFrom) { - let cps = Array.from(str); + var cps = Array.from(str); if (cps.length <= index) return Nothing; return Just(cps[index]); } else if (hasStringIterator) { - let iter = str[Symbol.iterator](); + var iter = str[Symbol.iterator](); for (;;) { - let { value, done } = iter.next(); - if (done) return Nothing; - if (i == 0) return Just(value); + var o = iter.next(); + if (o.done) return Nothing; + if (i == 0) return Just(o.value); --i; } } @@ -36,36 +36,19 @@ exports._codePointAt = function (fallback) { }; }; -exports._toCodePointArray = function (str) { - if (hasArrayFrom) { - return Array.from(str); - } else if (hasStringIterator) { - let accum = []; - let iter = str[Symbol.iterator](); - for (;;) { - let { value, done } = iter.next(); - if (done) return accum; - accum.push(value); - } - } - let accum = []; - for (let cuCount = 0; cuCount < str.length; ++cuCount) { - let cu = str[cuCount]; - let cp = cu; - if (isLead(cu) && cuCount + 1 < str.length) { - let lead = cu; - let trail = str[cuCount + 1]; - if (isTrail(trail)) { - cp = unsurrogate(lead, trail); +exports._toCodePointArray = function (fallback) { + return function (str) { + if (hasArrayFrom) { + return Array.from(str); + } else if (hasStringIterator) { + var accum = []; + var iter = str[Symbol.iterator](); + for (;;) { + var o = iter.next(); + if (o.done) return accum; + accum.push(o.value); } } - accum.push(cp); - } - return accum; + return fallback(str); + }; }; - -function isLead(cu) { return 0xD800 <= cu && cu <= 0xDBFF; } -function isTrail(cu) { return 0xDC00 <= cu && cu <= 0xDFFF; } -function unsurrogate(h, l) { - return (h - 0xD800) * 0x400 + (l - 0xDC00) + 0x10000; -} diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 7176282..49950fc 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -1,7 +1,7 @@ module CodePoints ( CodePoint() --, Pattern() - --, codePointAt + , codePointAt --, fromCodePointArray --, contains --, indexOf @@ -22,11 +22,17 @@ module CodePoints --, count --, split --, splitAt - --, toCodePointArray + , toCodePointArray ) where -import Prelude ((&&), (<=), (*), (+), (-)) +import Prelude ((&&), (*), (+), (-), (<$>), (<=)) import Data.Maybe (Maybe(Just, Nothing)) +import Data.String (toCharArray) +import Data.Unfoldable (unfoldr) +import Data.List (List(Cons, Nil), fromFoldable) +import Data.Tuple (Tuple(Tuple)) +import Data.Array (index) +import Data.Char (toCharCode) newtype CodePoint = CodePoint Int @@ -40,9 +46,11 @@ codePointToInt (CodePoint n) = n codePointFromSurrogatePair :: Int -> Int -> Maybe CodePoint codePointFromSurrogatePair lead trail | isLead lead && isTrail trail = Just (CodePoint (unsurrogate lead trail)) - where unsurrogate h l = (h - 0xD800) * 0x400 + (l - 0xDC00) + 0x10000 codePointFromSurrogatePair _ _ = Nothing +unsurrogate :: Int -> Int -> Int +unsurrogate h l = (h - 0xD800) * 0x400 + (l - 0xDC00) + 0x10000 + isLead :: Int -> Boolean isLead cu = 0xD800 <= cu && cu <= 0xDBFF @@ -50,7 +58,7 @@ isTrail :: Int -> Boolean isTrail cu = 0xDC00 <= cu && cu <= 0xDFFF codePointAt :: Int -> String -> Maybe CodePoint -codePointAt = _codePointAt (Just . CodePoint) Nothing +codePointAt = _codePointAt codePointAtFallback Just Nothing foreign import _codePointAt :: (Int -> String -> Maybe CodePoint) @@ -61,6 +69,21 @@ foreign import _codePointAt -> Maybe CodePoint codePointAtFallback :: Int -> String -> Maybe CodePoint -codePointAtFallback n s = CodePoint <$> index (toCodePointArray s) n +codePointAtFallback n s = index (toCodePointArray s) n + +toCodePointArray :: String -> Array CodePoint +toCodePointArray = _toCodePointArray toCodePointArrayFallback + +foreign import _toCodePointArray + :: (String -> Array CodePoint) + -> String + -> Array CodePoint -foreign import _toCodePointArray :: String -> Array CodePoint +toCodePointArrayFallback :: String -> Array CodePoint +toCodePointArrayFallback s = unfoldr decode (fromFoldable (toCharCode <$> toCharArray s)) + where + decode :: List Int -> Maybe (Tuple CodePoint (List Int)) + decode (Cons h (Cons l rest)) | isLead h && isTrail l + = Just (Tuple (CodePoint (unsurrogate h l)) rest) + decode (Cons c rest) = Just (Tuple (CodePoint c) rest) + decode Nil = Nothing From 25572de0630cc5e43c95dee1f242ea824911a2bc Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Thu, 25 May 2017 00:28:24 -0600 Subject: [PATCH 057/186] minor stuff --- src/Data/String/CodePoints.js | 1 + src/Data/String/CodePoints.purs | 24 ++++++++++++++---------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/Data/String/CodePoints.js b/src/Data/String/CodePoints.js index 16d499d..eeb3d2f 100644 --- a/src/Data/String/CodePoints.js +++ b/src/Data/String/CodePoints.js @@ -21,6 +21,7 @@ exports._codePointAt = function (fallback) { if (cps.length <= index) return Nothing; return Just(cps[index]); } else if (hasStringIterator) { + var i = index; var iter = str[Symbol.iterator](); for (;;) { var o = iter.next(); diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 49950fc..7ceccda 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -2,27 +2,29 @@ module CodePoints ( CodePoint() --, Pattern() , codePointAt - --, fromCodePointArray + , codePointFromInt + , codePointToInt --, contains + --, count + --, drop + --, dropWhile --, indexOf --, indexOf' --, lastIndexOf --, lastIndexOf' - --, uncons --, length - --, singleton --, replace --, replaceAll - --, take - --, takeWhile - --, drop - --, dropWhile - --, stripPrefix - --, stripSuffix - --, count + --, singleton --, split --, splitAt + --, stripPrefix + --, stripSuffix + --, take + --, takeWhile + --, uncons , toCodePointArray + --, fromCodePointArray ) where import Prelude ((&&), (*), (+), (-), (<$>), (<=)) @@ -57,6 +59,7 @@ isLead cu = 0xD800 <= cu && cu <= 0xDBFF isTrail :: Int -> Boolean isTrail cu = 0xDC00 <= cu && cu <= 0xDFFF + codePointAt :: Int -> String -> Maybe CodePoint codePointAt = _codePointAt codePointAtFallback Just Nothing @@ -71,6 +74,7 @@ foreign import _codePointAt codePointAtFallback :: Int -> String -> Maybe CodePoint codePointAtFallback n s = index (toCodePointArray s) n + toCodePointArray :: String -> Array CodePoint toCodePointArray = _toCodePointArray toCodePointArrayFallback From 8279da8f9d13f9a6b66514e62f570403380e156a Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Thu, 25 May 2017 15:50:17 -0600 Subject: [PATCH 058/186] count --- src/Data/String/CodePoints.purs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 7ceccda..aece075 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -5,7 +5,7 @@ module CodePoints , codePointFromInt , codePointToInt --, contains - --, count + , count --, drop --, dropWhile --, indexOf @@ -27,13 +27,13 @@ module CodePoints --, fromCodePointArray ) where -import Prelude ((&&), (*), (+), (-), (<$>), (<=)) +import Prelude ((&&), (*), (+), (-), (<$>), (<=), (<<<)) import Data.Maybe (Maybe(Just, Nothing)) import Data.String (toCharArray) import Data.Unfoldable (unfoldr) import Data.List (List(Cons, Nil), fromFoldable) import Data.Tuple (Tuple(Tuple)) -import Data.Array (index) +import Data.Array (index, length, filter) import Data.Char (toCharCode) newtype CodePoint = CodePoint Int @@ -75,6 +75,10 @@ codePointAtFallback :: Int -> String -> Maybe CodePoint codePointAtFallback n s = index (toCodePointArray s) n +count :: (CodePoint -> Boolean) -> String -> Int +count pred = length <<< filter pred <<< toCodePointArray + + toCodePointArray :: String -> Array CodePoint toCodePointArray = _toCodePointArray toCodePointArrayFallback From 292e0de444253949d163e9a96bbaaa673723752e Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Thu, 25 May 2017 16:19:03 -0600 Subject: [PATCH 059/186] drop and take --- src/Data/String/CodePoints.js | 36 +++++++++++++++++++++++++++++++++ src/Data/String/CodePoints.purs | 26 +++++++++++++++++++----- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/src/Data/String/CodePoints.js b/src/Data/String/CodePoints.js index eeb3d2f..2c87a56 100644 --- a/src/Data/String/CodePoints.js +++ b/src/Data/String/CodePoints.js @@ -4,6 +4,7 @@ var hasStringIterator = Symbol != null && typeof Symbol.iterator !== 'undefined' && typeof String.prototype[Symbol.iterator] === 'function'; +var hasFromCodePoint = typeof String.prototype.fromCodePoint === 'function'; exports._codePointAt = function (fallback) { return function (Just) { @@ -37,6 +38,26 @@ exports._codePointAt = function (fallback) { }; }; +exports._take = function (fallback) { + return function (n) { + return function (str) { + if (hasArrayFrom) { + return Array.from(str); + } else if (hasStringIterator) { + var accum = ""; + var iter = str[Symbol.iterator](); + for (var i = 0; i < n; ++i) { + var o = iter.next(); + if (o.done) return accum; + accum += o.value; + } + return accum; + } + return fallback(str); + }; + }; +}; + exports._toCodePointArray = function (fallback) { return function (str) { if (hasArrayFrom) { @@ -53,3 +74,18 @@ exports._toCodePointArray = function (fallback) { return fallback(str); }; }; + + +exports.fromCodePointArray = function (cps) { + if (hasFromCodePoint) { + return String.fromCodePoint.apply(cps); + } + return cps.map(fromCodePoint).join(''); +}; + +function fromCodePoint(cp) { + if (cp <= 0xFFFF) return String.fromCharCode(cp); + var cu1 = String.fromCharCode(Math.floor((cp - 0x10000) / 0x400) + 0xD800); + var cu2 = String.fromCharCode((cp - 0x10000) % 0x400 + 0xDC00); + return cu1 + cu2; +} diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index aece075..eb52f96 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -6,7 +6,7 @@ module CodePoints , codePointToInt --, contains , count - --, drop + , drop --, dropWhile --, indexOf --, indexOf' @@ -20,7 +20,7 @@ module CodePoints --, splitAt --, stripPrefix --, stripSuffix - --, take + , take --, takeWhile --, uncons , toCodePointArray @@ -33,7 +33,7 @@ import Data.String (toCharArray) import Data.Unfoldable (unfoldr) import Data.List (List(Cons, Nil), fromFoldable) import Data.Tuple (Tuple(Tuple)) -import Data.Array (index, length, filter) +import Data.Array as Array import Data.Char (toCharCode) newtype CodePoint = CodePoint Int @@ -72,11 +72,24 @@ foreign import _codePointAt -> Maybe CodePoint codePointAtFallback :: Int -> String -> Maybe CodePoint -codePointAtFallback n s = index (toCodePointArray s) n +codePointAtFallback n s = Array.index (toCodePointArray s) n count :: (CodePoint -> Boolean) -> String -> Int -count pred = length <<< filter pred <<< toCodePointArray +count pred = Array.length <<< Array.filter pred <<< toCodePointArray + + +drop :: Int -> String -> String +drop n s = fromCodePointArray (Array.drop n (toCodePointArray s)) + + +take :: Int -> String -> String +take = _take takeFallback + +foreign import _take :: (Int -> String -> String) -> Int -> String -> String + +takeFallback :: Int -> String -> String +takeFallback n s = fromCodePointArray (Array.take n (toCodePointArray s)) toCodePointArray :: String -> Array CodePoint @@ -95,3 +108,6 @@ toCodePointArrayFallback s = unfoldr decode (fromFoldable (toCharCode <$> toChar = Just (Tuple (CodePoint (unsurrogate h l)) rest) decode (Cons c rest) = Just (Tuple (CodePoint c) rest) decode Nil = Nothing + + +foreign import fromCodePointArray :: Array CodePoint -> String From fd91b0ba09f88318611d503cd89eef6de947c862 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Thu, 25 May 2017 17:00:15 -0600 Subject: [PATCH 060/186] length --- src/Data/String/CodePoints.purs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index eb52f96..dd719a7 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -12,7 +12,7 @@ module CodePoints --, indexOf' --, lastIndexOf --, lastIndexOf' - --, length + , length --, replace --, replaceAll --, singleton @@ -83,6 +83,10 @@ drop :: Int -> String -> String drop n s = fromCodePointArray (Array.drop n (toCodePointArray s)) +length :: String -> Int +length = Array.length <<< toCodePointArray + + take :: Int -> String -> String take = _take takeFallback From 83876413a8dd5b907c20f51944838a0d1602d03d Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Thu, 25 May 2017 17:02:19 -0600 Subject: [PATCH 061/186] singleton --- src/Data/String/CodePoints.js | 2 ++ src/Data/String/CodePoints.purs | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Data/String/CodePoints.js b/src/Data/String/CodePoints.js index 2c87a56..1d3469a 100644 --- a/src/Data/String/CodePoints.js +++ b/src/Data/String/CodePoints.js @@ -38,6 +38,8 @@ exports._codePointAt = function (fallback) { }; }; +exports.singleton = fromCodePoint; + exports._take = function (fallback) { return function (n) { return function (str) { diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index dd719a7..4f20f8f 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -15,7 +15,7 @@ module CodePoints , length --, replace --, replaceAll - --, singleton + , singleton --, split --, splitAt --, stripPrefix @@ -87,6 +87,9 @@ length :: String -> Int length = Array.length <<< toCodePointArray +foreign import singleton :: CodePoint -> String + + take :: Int -> String -> String take = _take takeFallback From fb473871d8f7620bedda52e6802f86aa5bf03bb8 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Fri, 26 May 2017 10:04:05 -0600 Subject: [PATCH 062/186] splitAt --- src/Data/String/CodePoints.purs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 4f20f8f..def5b99 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -17,7 +17,7 @@ module CodePoints --, replaceAll , singleton --, split - --, splitAt + , splitAt --, stripPrefix --, stripSuffix , take @@ -27,7 +27,7 @@ module CodePoints --, fromCodePointArray ) where -import Prelude ((&&), (*), (+), (-), (<$>), (<=), (<<<)) +import Prelude ((&&), (||), (*), (+), (-), (<$>), (<), (<=), (<<<)) import Data.Maybe (Maybe(Just, Nothing)) import Data.String (toCharArray) import Data.Unfoldable (unfoldr) @@ -90,6 +90,17 @@ length = Array.length <<< toCodePointArray foreign import singleton :: CodePoint -> String +splitAt :: Int -> String -> Maybe { 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) + } + + take :: Int -> String -> String take = _take takeFallback From 5a6cfd069df05db7f452fec062590b0155292143 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Fri, 26 May 2017 10:16:10 -0600 Subject: [PATCH 063/186] use String.fromCodePoint in singleton implementation when available --- src/Data/String/CodePoints.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Data/String/CodePoints.js b/src/Data/String/CodePoints.js index 1d3469a..4c0cc48 100644 --- a/src/Data/String/CodePoints.js +++ b/src/Data/String/CodePoints.js @@ -38,7 +38,7 @@ exports._codePointAt = function (fallback) { }; }; -exports.singleton = fromCodePoint; +exports.singleton = hasFromCodePoint ? String.fromCodePoint : fromCodePoint; exports._take = function (fallback) { return function (n) { @@ -80,7 +80,7 @@ exports._toCodePointArray = function (fallback) { exports.fromCodePointArray = function (cps) { if (hasFromCodePoint) { - return String.fromCodePoint.apply(cps); + return String.fromCodePoint.apply(String, cps); } return cps.map(fromCodePoint).join(''); }; From 3003c090d6fcc53e90222bca61f600569cac916e Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Fri, 26 May 2017 14:44:05 -0600 Subject: [PATCH 064/186] re-export Data.String --- src/Data/String/CodePoints.purs | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index def5b99..2c6887b 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -1,40 +1,36 @@ module CodePoints ( CodePoint() - --, Pattern() , codePointAt , codePointFromInt , codePointToInt - --, contains , count , drop --, dropWhile + , fromCodePointArray --, indexOf --, indexOf' --, lastIndexOf --, lastIndexOf' , length - --, replace - --, replaceAll , singleton - --, split , splitAt - --, stripPrefix - --, stripSuffix , take --, takeWhile --, uncons , toCodePointArray - --, fromCodePointArray + + , module StringReExports ) where -import Prelude ((&&), (||), (*), (+), (-), (<$>), (<), (<=), (<<<)) +import Data.Array as Array +import Data.Char (toCharCode) +import Data.List (List(Cons, Nil), fromFoldable) import Data.Maybe (Maybe(Just, Nothing)) import Data.String (toCharArray) -import Data.Unfoldable (unfoldr) -import Data.List (List(Cons, Nil), fromFoldable) +import Data.String hiding (count, drop, dropWhile, indexOf, indexOf', lastIndexOf, lastIndexOf', length, singleton, splitAt, take, takeWhile, uncons) as StringReExports import Data.Tuple (Tuple(Tuple)) -import Data.Array as Array -import Data.Char (toCharCode) +import Data.Unfoldable (unfoldr) +import Prelude ((&&), (||), (*), (+), (-), (<$>), (<), (<=), (<<<)) newtype CodePoint = CodePoint Int From 75117d2043a00b9bbe03798d1d4c08a37eee6ae2 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Fri, 26 May 2017 14:56:12 -0600 Subject: [PATCH 065/186] uncons --- src/Data/String/CodePoints.purs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 2c6887b..a8aa8fc 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -16,7 +16,7 @@ module CodePoints , splitAt , take --, takeWhile - --, uncons + , uncons , toCodePointArray , module StringReExports @@ -79,6 +79,9 @@ drop :: Int -> String -> String drop n s = fromCodePointArray (Array.drop n (toCodePointArray s)) +foreign import fromCodePointArray :: Array CodePoint -> String + + length :: String -> Int length = Array.length <<< toCodePointArray @@ -124,4 +127,5 @@ toCodePointArrayFallback s = unfoldr decode (fromFoldable (toCharCode <$> toChar decode Nil = Nothing -foreign import fromCodePointArray :: Array CodePoint -> String +uncons :: String -> Maybe { head :: CodePoint, tail :: String } +uncons s = { head: _, tail: drop 1 s } <$> codePointAt 0 s From ecfbf0bc6825b6af3492baa07cc3acedf69857c8 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Fri, 26 May 2017 14:57:24 -0600 Subject: [PATCH 066/186] re-arrange imports --- src/Data/String/CodePoints.purs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index a8aa8fc..3a6d180 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -1,5 +1,6 @@ module CodePoints - ( CodePoint() + ( module StringReExports + , CodePoint() , codePointAt , codePointFromInt , codePointToInt @@ -16,10 +17,8 @@ module CodePoints , splitAt , take --, takeWhile - , uncons , toCodePointArray - - , module StringReExports + , uncons ) where import Data.Array as Array From d5b6d92b5e140cc8843e508caf6db015d18fb15c Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Fri, 26 May 2017 15:00:01 -0600 Subject: [PATCH 067/186] re-arrange JS exports --- src/Data/String/CodePoints.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Data/String/CodePoints.js b/src/Data/String/CodePoints.js index 4c0cc48..0851961 100644 --- a/src/Data/String/CodePoints.js +++ b/src/Data/String/CodePoints.js @@ -38,6 +38,13 @@ exports._codePointAt = function (fallback) { }; }; +exports.fromCodePointArray = function (cps) { + if (hasFromCodePoint) { + return String.fromCodePoint.apply(String, cps); + } + return cps.map(fromCodePoint).join(''); +}; + exports.singleton = hasFromCodePoint ? String.fromCodePoint : fromCodePoint; exports._take = function (fallback) { @@ -77,14 +84,6 @@ exports._toCodePointArray = function (fallback) { }; }; - -exports.fromCodePointArray = function (cps) { - if (hasFromCodePoint) { - return String.fromCodePoint.apply(String, cps); - } - return cps.map(fromCodePoint).join(''); -}; - function fromCodePoint(cp) { if (cp <= 0xFFFF) return String.fromCharCode(cp); var cu1 = String.fromCharCode(Math.floor((cp - 0x10000) / 0x400) + 0xD800); From 8860295f284b635cc2b1cb5b8407d76c8d617f4d Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Fri, 26 May 2017 17:38:55 -0600 Subject: [PATCH 068/186] fix count; implement dropWhile and takeWhile --- src/Data/String/CodePoints.js | 24 ++++++++++++++++++++++++ src/Data/String/CodePoints.purs | 31 ++++++++++++++++++++++++------- 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/src/Data/String/CodePoints.js b/src/Data/String/CodePoints.js index 0851961..df9d144 100644 --- a/src/Data/String/CodePoints.js +++ b/src/Data/String/CodePoints.js @@ -38,6 +38,30 @@ exports._codePointAt = function (fallback) { }; }; +exports._count = function (isLead) { + return function (isTrail) { + return function (unsurrogate) { + return function (pred) { + return function (str) { + for (var cuCount = 0, cpCount = 0; cuCount < str.length; ++cuCount, ++cpCount) { + var lead = str.charCodeAt(cuCount); + var cp = lead; + if (isLead(lead) && cuCount + 1 < str.length) { + var trail = str.charCodeAt(cuCount + 1); + if (isTrail(trail)) { + cp = unsurrogate(lead, trail); + ++cuCount; + } + } + if (!pred(cp)) return cpCount; + } + return str.length; + }; + }; + }; + }; +}; + exports.fromCodePointArray = function (cps) { if (hasFromCodePoint) { return String.fromCodePoint.apply(String, cps); diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 3a6d180..0528796 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -6,7 +6,7 @@ module CodePoints , codePointToInt , count , drop - --, dropWhile + , dropWhile , fromCodePointArray --, indexOf --, indexOf' @@ -16,7 +16,7 @@ module CodePoints , singleton , splitAt , take - --, takeWhile + , takeWhile , toCodePointArray , uncons ) where @@ -31,6 +31,7 @@ import Data.Tuple (Tuple(Tuple)) import Data.Unfoldable (unfoldr) import Prelude ((&&), (||), (*), (+), (-), (<$>), (<), (<=), (<<<)) + newtype CodePoint = CodePoint Int codePointFromInt :: Int -> Maybe CodePoint @@ -42,11 +43,11 @@ codePointToInt (CodePoint n) = n codePointFromSurrogatePair :: Int -> Int -> Maybe CodePoint codePointFromSurrogatePair lead trail | isLead lead && isTrail trail - = Just (CodePoint (unsurrogate lead trail)) + = Just (unsurrogate lead trail) codePointFromSurrogatePair _ _ = Nothing -unsurrogate :: Int -> Int -> Int -unsurrogate h l = (h - 0xD800) * 0x400 + (l - 0xDC00) + 0x10000 +unsurrogate :: Int -> Int -> CodePoint +unsurrogate h l = CodePoint ((h - 0xD800) * 0x400 + (l - 0xDC00) + 0x10000) isLead :: Int -> Boolean isLead cu = 0xD800 <= cu && cu <= 0xDBFF @@ -71,13 +72,25 @@ codePointAtFallback n s = Array.index (toCodePointArray s) n count :: (CodePoint -> Boolean) -> String -> Int -count pred = Array.length <<< Array.filter pred <<< toCodePointArray +count = _count isLead isTrail unsurrogate + +foreign import _count + :: (Int -> Boolean) + -> (Int -> Boolean) + -> (Int -> Int -> CodePoint) + -> (CodePoint -> Boolean) + -> String + -> Int drop :: Int -> String -> String drop n s = fromCodePointArray (Array.drop n (toCodePointArray s)) +dropWhile :: (CodePoint -> Boolean) -> String -> String +dropWhile p s = drop (count p s) s + + foreign import fromCodePointArray :: Array CodePoint -> String @@ -108,6 +121,10 @@ takeFallback :: Int -> String -> String takeFallback n s = fromCodePointArray (Array.take n (toCodePointArray s)) +takeWhile :: (CodePoint -> Boolean) -> String -> String +takeWhile p s = take (count p s) s + + toCodePointArray :: String -> Array CodePoint toCodePointArray = _toCodePointArray toCodePointArrayFallback @@ -121,7 +138,7 @@ toCodePointArrayFallback s = unfoldr decode (fromFoldable (toCharCode <$> toChar where decode :: List Int -> Maybe (Tuple CodePoint (List Int)) decode (Cons h (Cons l rest)) | isLead h && isTrail l - = Just (Tuple (CodePoint (unsurrogate h l)) rest) + = Just (Tuple (unsurrogate h l) rest) decode (Cons c rest) = Just (Tuple (CodePoint c) rest) decode Nil = Nothing From a6855b4fc770eb3afb6030ef4be7ce9964349208 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Fri, 26 May 2017 18:44:31 -0600 Subject: [PATCH 069/186] indexOf and lastIndexOf --- src/Data/String/CodePoints.purs | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 0528796..68427b3 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -8,10 +8,10 @@ module CodePoints , drop , dropWhile , fromCodePointArray - --, indexOf - --, indexOf' - --, lastIndexOf - --, lastIndexOf' + , indexOf + , indexOf' + , lastIndexOf + , lastIndexOf' , length , singleton , splitAt @@ -25,7 +25,7 @@ import Data.Array as Array import Data.Char (toCharCode) import Data.List (List(Cons, Nil), fromFoldable) import Data.Maybe (Maybe(Just, Nothing)) -import Data.String (toCharArray) +import Data.String as String import Data.String hiding (count, drop, dropWhile, indexOf, indexOf', lastIndexOf, lastIndexOf', length, singleton, splitAt, take, takeWhile, uncons) as StringReExports import Data.Tuple (Tuple(Tuple)) import Data.Unfoldable (unfoldr) @@ -94,6 +94,22 @@ dropWhile p s = drop (count p s) s foreign import fromCodePointArray :: Array CodePoint -> String +indexOf :: String.Pattern -> String -> Maybe Int +indexOf p s = (\i -> length (String.take i s)) <$> String.indexOf p s + + +indexOf' :: String.Pattern -> Int -> String -> Maybe Int +indexOf' p i s = (\k -> length (String.take k s)) <$> String.indexOf' p i s + + +lastIndexOf :: String.Pattern -> String -> Maybe Int +lastIndexOf p s = (\i -> length (String.take i s)) <$> String.lastIndexOf p s + + +lastIndexOf' :: String.Pattern -> Int -> String -> Maybe Int +lastIndexOf' p i s = (\k -> length (String.take k s)) <$> String.lastIndexOf' p i s + + length :: String -> Int length = Array.length <<< toCodePointArray @@ -134,7 +150,7 @@ foreign import _toCodePointArray -> Array CodePoint toCodePointArrayFallback :: String -> Array CodePoint -toCodePointArrayFallback s = unfoldr decode (fromFoldable (toCharCode <$> toCharArray s)) +toCodePointArrayFallback s = unfoldr decode (fromFoldable (toCharCode <$> String.toCharArray s)) where decode :: List Int -> Maybe (Tuple CodePoint (List Int)) decode (Cons h (Cons l rest)) | isLead h && isTrail l From 8c55257a3f12632f12e8c7027691225896571136 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Sat, 27 May 2017 00:17:53 -0600 Subject: [PATCH 070/186] add some initial tests and fix some bugs --- src/Data/String/CodePoints.js | 70 ++++++------- src/Data/String/CodePoints.purs | 9 +- test/Test/Data/String/CodePoints.purs | 143 ++++++++++++++++++++++++++ test/Test/Main.purs | 2 + 4 files changed, 186 insertions(+), 38 deletions(-) create mode 100644 test/Test/Data/String/CodePoints.purs diff --git a/src/Data/String/CodePoints.js b/src/Data/String/CodePoints.js index df9d144..fd75ab5 100644 --- a/src/Data/String/CodePoints.js +++ b/src/Data/String/CodePoints.js @@ -5,30 +5,25 @@ var hasStringIterator = typeof Symbol.iterator !== 'undefined' && typeof String.prototype[Symbol.iterator] === 'function'; var hasFromCodePoint = typeof String.prototype.fromCodePoint === 'function'; +var hasCodePointAt = typeof String.prototype.codePointAt === 'function'; exports._codePointAt = function (fallback) { return function (Just) { return function (Nothing) { - return function (relIndex) { + return function (index) { return function (str) { var length = str.length; - if (length <= relIndex) return Nothing; - var index = relIndex < 0 ? ((relIndex % length) + length) % length : relIndex; - if (typeof String.prototype.codePointAt === 'function') { - var cp = str.codePointAt(index); - return cp == null ? Nothing : Just(cp); - } else if (hasArrayFrom) { + if (index < 0 || index >= length) return Nothing; + if (hasArrayFrom && hasCodePointAt) { var cps = Array.from(str); - if (cps.length <= index) return Nothing; - return Just(cps[index]); + if (index >= cps.length) return Nothing; + return Just(cps[index].codePointAt(0)); } else if (hasStringIterator) { - var i = index; var iter = str[Symbol.iterator](); - for (;;) { + for (var i = index;; --i) { var o = iter.next(); if (o.done) return Nothing; if (i == 0) return Just(o.value); - --i; } } return fallback(index)(str); @@ -43,40 +38,41 @@ exports._count = function (isLead) { return function (unsurrogate) { return function (pred) { return function (str) { - for (var cuCount = 0, cpCount = 0; cuCount < str.length; ++cuCount, ++cpCount) { + var cpCount = 0; + for (var cuCount = 0; cuCount < str.length; ++cuCount) { var lead = str.charCodeAt(cuCount); var cp = lead; if (isLead(lead) && cuCount + 1 < str.length) { var trail = str.charCodeAt(cuCount + 1); if (isTrail(trail)) { - cp = unsurrogate(lead, trail); + cp = unsurrogate(lead)(trail); ++cuCount; } } if (!pred(cp)) return cpCount; + ++cpCount; } - return str.length; + return cpCount; }; }; }; }; }; -exports.fromCodePointArray = function (cps) { - if (hasFromCodePoint) { - return String.fromCodePoint.apply(String, cps); - } - return cps.map(fromCodePoint).join(''); -}; +exports.fromCodePointArray = hasFromCodePoint + ? function (cps) { return String.fromCodePoint.apply(String, cps); } + : function (cps) { return cps.map(fromCodePoint).join(''); }; exports.singleton = hasFromCodePoint ? String.fromCodePoint : fromCodePoint; exports._take = function (fallback) { return function (n) { - return function (str) { - if (hasArrayFrom) { - return Array.from(str); - } else if (hasStringIterator) { + if (hasArrayFrom) { + return function (str) { + return Array.from(str).slice(0, n).join(''); + }; + } else if (hasStringIterator) { + return function (str) { var accum = ""; var iter = str[Symbol.iterator](); for (var i = 0; i < n; ++i) { @@ -85,27 +81,29 @@ exports._take = function (fallback) { accum += o.value; } return accum; - } - return fallback(str); - }; + }; + } + return fallback; }; }; exports._toCodePointArray = function (fallback) { - return function (str) { - if (hasArrayFrom) { - return Array.from(str); - } else if (hasStringIterator) { + if (hasArrayFrom && hasCodePointAt) { + return function (str) { + return Array.from(str, function (x) { return x.codePointAt(0); }); + }; + } else if (hasStringIterator && hasCodePointAt) { + return function (str) { var accum = []; var iter = str[Symbol.iterator](); for (;;) { var o = iter.next(); if (o.done) return accum; - accum.push(o.value); + accum.push(o.value.codePointAt(0)); } - } - return fallback(str); - }; + }; + } + return fallback; }; function fromCodePoint(cp) { diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 68427b3..6bdf282 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -1,4 +1,4 @@ -module CodePoints +module Data.String.CodePoints ( module StringReExports , CodePoint() , codePointAt @@ -25,15 +25,20 @@ import Data.Array as Array import Data.Char (toCharCode) import Data.List (List(Cons, Nil), fromFoldable) import Data.Maybe (Maybe(Just, Nothing)) +import Data.Newtype (class Newtype) import Data.String as String import Data.String hiding (count, drop, dropWhile, indexOf, indexOf', lastIndexOf, lastIndexOf', length, singleton, splitAt, take, takeWhile, uncons) as StringReExports import Data.Tuple (Tuple(Tuple)) import Data.Unfoldable (unfoldr) -import Prelude ((&&), (||), (*), (+), (-), (<$>), (<), (<=), (<<<)) +import Prelude (class Eq, class Ord, (&&), (||), (*), (+), (-), (<$>), (<), (<=), (<<<)) newtype CodePoint = CodePoint Int +derive instance eqCodePoint :: Eq CodePoint +derive instance ordCodePoint :: Ord CodePoint +derive instance newtypeCodePoint :: Newtype CodePoint _ + codePointFromInt :: Int -> Maybe CodePoint codePointFromInt n | 0 <= n && n <= 0x10FFFF = Just (CodePoint n) codePointFromInt n = Nothing diff --git a/test/Test/Data/String/CodePoints.purs b/test/Test/Data/String/CodePoints.purs new file mode 100644 index 0000000..28e289c --- /dev/null +++ b/test/Test/Data/String/CodePoints.purs @@ -0,0 +1,143 @@ +module Test.Data.String.CodePoints (testStringCodePoints) where + +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.String.CodePoints + +import Test.Assert (ASSERT, assert) + +str :: String +str = "a\xDC00\xD800\xD800\x16805\x16A06\&z" + +testStringCodePoints :: forall eff. Eff (console :: CONSOLE, assert :: ASSERT | eff) Unit +testStringCodePoints = do + log "codePointAt" + assert $ codePointAt (-1) str == Nothing + assert $ codePointAt 0 str == (codePointFromInt 0x61) + assert $ codePointAt 1 str == (codePointFromInt 0xDC00) + assert $ codePointAt 2 str == (codePointFromInt 0xD800) + assert $ codePointAt 3 str == (codePointFromInt 0xD800) + assert $ codePointAt 4 str == (codePointFromInt 0x16805) + assert $ codePointAt 5 str == (codePointFromInt 0x16A06) + assert $ codePointAt 6 str == (codePointFromInt 0x7A) + assert $ codePointAt 7 str == Nothing + + log "count" + assert $ count (\_ -> true) "" == 0 + assert $ count (\_ -> false) str == 0 + assert $ count (\_ -> true) str == 7 + assert $ count (\x -> codePointToInt x < 0xFFFF) str == 4 + assert $ count (\x -> codePointToInt x < 0xDC00) str == 1 + + log "drop" + assert $ drop (-1) str == str + assert $ drop 0 str == str + assert $ drop 1 str == "\xDC00\xD800\xD800\x16805\x16A06\&z" + assert $ drop 2 str == "\xD800\xD800\x16805\x16A06\&z" + assert $ drop 3 str == "\xD800\x16805\x16A06\&z" + assert $ drop 4 str == "\x16805\x16A06\&z" + assert $ drop 5 str == "\x16A06\&z" + assert $ drop 6 str == "z" + assert $ drop 7 str == "" + assert $ drop 8 str == "" + + log "dropWhile" + assert $ dropWhile (\c -> true) str == "" + assert $ dropWhile (\c -> false) str == str + assert $ dropWhile (\c -> codePointToInt c < 0xFFFF) str == "\x16805\x16A06\&z" + assert $ dropWhile (\c -> codePointToInt c < 0xDC00) str == "\xDC00\xD800\xD800\x16805\x16A06\&z" + + log "indexOf" + assert $ indexOf (Pattern "") "" == Just 0 + assert $ indexOf (Pattern "") str == Just 0 + assert $ indexOf (Pattern "a") str == Just 0 + assert $ indexOf (Pattern "\xDC00\xD800\xD800") str == Just 1 + assert $ indexOf (Pattern "\xD800") str == Just 2 + assert $ indexOf (Pattern "\xD800\xD800") str == Just 2 + assert $ indexOf (Pattern "\xD800\xD81A") str == Just 3 + assert $ indexOf (Pattern "\xD800\x16805") str == Just 3 + assert $ indexOf (Pattern "\x16805") str == Just 4 + assert $ indexOf (Pattern "\x16A06") str == Just 5 + assert $ indexOf (Pattern "z") str == Just 6 + assert $ indexOf (Pattern "\0") str == Nothing + assert $ indexOf (Pattern "\xD81A") str == Just 4 + -- TODO: Should this be Nothing? It matches the trail surrogate of a surrogate pair. + -- It'd be nice if (drop (indexOf pattern str) str) was guaranteed to start with pattern. + -- If we change this, we'll also need to add a matching contains implementation to the CodePoints module. + -- I vote we just delete the test. Passing surrogate halves to the CodePoints functions should not be supported. + assert $ indexOf (Pattern "\xDC05") str == Just 5 + +-- log "singleton" +-- assert $ singleton 'a' == "a" +-- +-- log "takeWhile" +-- assert $ takeWhile (\c -> true) "abc" == "abc" +-- assert $ takeWhile (\c -> false) "abc" == "" +-- assert $ takeWhile (\c -> c /= 'b') "aabbcc" == "aa" +-- +-- log "indexOf'" +-- assert $ indexOf' (Pattern "") 0 "" == Just 0 +-- assert $ indexOf' (Pattern "") (-1) "ab" == Nothing +-- assert $ indexOf' (Pattern "") 0 "ab" == Just 0 +-- assert $ indexOf' (Pattern "") 1 "ab" == Just 1 +-- assert $ indexOf' (Pattern "") 2 "ab" == Just 2 +-- assert $ indexOf' (Pattern "") 3 "ab" == Nothing +-- assert $ indexOf' (Pattern "bc") 0 "abcd" == Just 1 +-- assert $ indexOf' (Pattern "bc") 1 "abcd" == Just 1 +-- assert $ indexOf' (Pattern "bc") 2 "abcd" == Nothing +-- assert $ indexOf' (Pattern "cb") 0 "abcd" == Nothing +-- +-- log "lastIndexOf" +-- assert $ lastIndexOf (Pattern "") "" == Just 0 +-- assert $ lastIndexOf (Pattern "") "abcd" == Just 4 +-- assert $ lastIndexOf (Pattern "bc") "abcd" == Just 1 +-- assert $ lastIndexOf (Pattern "cb") "abcd" == Nothing +-- +-- log "lastIndexOf'" +-- assert $ lastIndexOf' (Pattern "") 0 "" == Just 0 +-- assert $ lastIndexOf' (Pattern "") (-1) "ab" == Nothing +-- assert $ lastIndexOf' (Pattern "") 0 "ab" == Just 0 +-- assert $ lastIndexOf' (Pattern "") 1 "ab" == Just 1 +-- assert $ lastIndexOf' (Pattern "") 2 "ab" == Just 2 +-- assert $ lastIndexOf' (Pattern "") 3 "ab" == Nothing +-- assert $ lastIndexOf' (Pattern "bc") 0 "abcd" == Nothing +-- assert $ lastIndexOf' (Pattern "bc") 1 "abcd" == Just 1 +-- assert $ lastIndexOf' (Pattern "bc") 2 "abcd" == Just 1 +-- assert $ lastIndexOf' (Pattern "cb") 0 "abcd" == Nothing +-- +-- log "length" +-- assert $ length "" == 0 +-- assert $ length "a" == 1 +-- assert $ length "ab" == 2 +-- +-- log "take" +-- assert $ take 0 "ab" == "" +-- assert $ take 1 "ab" == "a" +-- assert $ take 2 "ab" == "ab" +-- assert $ take 3 "ab" == "ab" +-- assert $ take (-1) "ab" == "" +-- +-- log "count" +-- assert $ count (_ == 'a') "" == 0 +-- assert $ count (_ == 'a') "ab" == 1 +-- assert $ count (_ == 'a') "aaab" == 3 +-- assert $ count (_ == 'a') "abaa" == 1 +-- +-- log "splitAt" +-- 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 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 diff --git a/test/Test/Main.purs b/test/Test/Main.purs index b2c7f50..8260cc6 100644 --- a/test/Test/Main.purs +++ b/test/Test/Main.purs @@ -8,6 +8,7 @@ import Control.Monad.Eff.Console (CONSOLE) import Test.Assert (ASSERT) import Test.Data.Char (testChar) import Test.Data.String (testString) +import Test.Data.String.CodePoints (testStringCodePoints) import Test.Data.String.Regex (testStringRegex) import Test.Data.String.Unsafe (testStringUnsafe) import Test.Data.String.CaseInsensitive (testCaseInsensitiveString) @@ -16,6 +17,7 @@ main :: Eff (console :: CONSOLE, assert :: ASSERT) Unit main = do testChar testString + testStringCodePoints testStringUnsafe testStringRegex testCaseInsensitiveString From a26afdf1cf62120f6b3459d5feacc22d6ff93f8b Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Sat, 27 May 2017 00:32:42 -0600 Subject: [PATCH 071/186] trailing whitespace --- src/Data/String/CodePoints.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Data/String/CodePoints.js b/src/Data/String/CodePoints.js index fd75ab5..d453de6 100644 --- a/src/Data/String/CodePoints.js +++ b/src/Data/String/CodePoints.js @@ -1,5 +1,5 @@ var hasArrayFrom = typeof Array.from === 'function'; -var hasStringIterator = +var hasStringIterator = typeof Symbol !== 'undefined' && Symbol != null && typeof Symbol.iterator !== 'undefined' && From c1ff8c579316c6a34c605a5d702811597ea1cfcd Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Sat, 27 May 2017 16:11:14 -0600 Subject: [PATCH 072/186] finished the tests --- src/Data/String/CodePoints.js | 2 +- src/Data/String/CodePoints.purs | 10 +- test/Test/Data/String/CodePoints.purs | 189 ++++++++++++++++---------- 3 files changed, 126 insertions(+), 75 deletions(-) diff --git a/src/Data/String/CodePoints.js b/src/Data/String/CodePoints.js index d453de6..c1d9163 100644 --- a/src/Data/String/CodePoints.js +++ b/src/Data/String/CodePoints.js @@ -69,7 +69,7 @@ exports._take = function (fallback) { return function (n) { if (hasArrayFrom) { return function (str) { - return Array.from(str).slice(0, n).join(''); + return Array.from(str).slice(0, Math.max(0, n)).join(''); }; } else if (hasStringIterator) { return function (str) { diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 6bdf282..f6b49ed 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -104,7 +104,9 @@ indexOf p s = (\i -> length (String.take i s)) <$> String.indexOf p s indexOf' :: String.Pattern -> Int -> String -> Maybe Int -indexOf' p i s = (\k -> length (String.take k s)) <$> String.indexOf' p i s +indexOf' p i s = + let s' = drop i s in + (\k -> i + length (String.take k s')) <$> String.indexOf p s' lastIndexOf :: String.Pattern -> String -> Maybe Int @@ -112,7 +114,9 @@ lastIndexOf p s = (\i -> length (String.take i s)) <$> String.lastIndexOf p s lastIndexOf' :: String.Pattern -> Int -> String -> Maybe Int -lastIndexOf' p i s = (\k -> length (String.take k s)) <$> String.lastIndexOf' p i s +lastIndexOf' p i s = + let s' = drop i s in + (\k -> i + length (String.take k s')) <$> String.lastIndexOf p s' length :: String -> Int @@ -125,7 +129,7 @@ foreign import singleton :: CodePoint -> String splitAt :: Int -> String -> Maybe { before :: String, after :: String } splitAt i s = let cps = toCodePointArray s in - if i < 0 || Array.length cps <= i + if i < 0 || Array.length cps < i then Nothing else Just { before: fromCodePointArray (Array.take i cps), diff --git a/test/Test/Data/String/CodePoints.purs b/test/Test/Data/String/CodePoints.purs index 28e289c..d7dac6a 100644 --- a/test/Test/Data/String/CodePoints.purs +++ b/test/Test/Data/String/CodePoints.purs @@ -1,6 +1,6 @@ module Test.Data.String.CodePoints (testStringCodePoints) where -import Prelude (Unit, Ordering(..), (==), ($), discard, negate, not, (/=), (&&), (<)) +import Prelude (Unit, discard, negate, (==), ($), (&&), (<), (<$>)) import Control.Monad.Eff (Eff) import Control.Monad.Eff.Console (CONSOLE, log) @@ -54,6 +54,7 @@ testStringCodePoints = do log "indexOf" assert $ indexOf (Pattern "") "" == Just 0 assert $ indexOf (Pattern "") str == Just 0 + assert $ indexOf (Pattern str) str == Just 0 assert $ indexOf (Pattern "a") str == Just 0 assert $ indexOf (Pattern "\xDC00\xD800\xD800") str == Just 1 assert $ indexOf (Pattern "\xD800") str == Just 2 @@ -71,73 +72,119 @@ testStringCodePoints = do -- I vote we just delete the test. Passing surrogate halves to the CodePoints functions should not be supported. assert $ indexOf (Pattern "\xDC05") str == Just 5 --- log "singleton" --- assert $ singleton 'a' == "a" --- --- log "takeWhile" --- assert $ takeWhile (\c -> true) "abc" == "abc" --- assert $ takeWhile (\c -> false) "abc" == "" --- assert $ takeWhile (\c -> c /= 'b') "aabbcc" == "aa" --- --- log "indexOf'" --- assert $ indexOf' (Pattern "") 0 "" == Just 0 --- assert $ indexOf' (Pattern "") (-1) "ab" == Nothing --- assert $ indexOf' (Pattern "") 0 "ab" == Just 0 --- assert $ indexOf' (Pattern "") 1 "ab" == Just 1 --- assert $ indexOf' (Pattern "") 2 "ab" == Just 2 --- assert $ indexOf' (Pattern "") 3 "ab" == Nothing --- assert $ indexOf' (Pattern "bc") 0 "abcd" == Just 1 --- assert $ indexOf' (Pattern "bc") 1 "abcd" == Just 1 --- assert $ indexOf' (Pattern "bc") 2 "abcd" == Nothing --- assert $ indexOf' (Pattern "cb") 0 "abcd" == Nothing --- --- log "lastIndexOf" --- assert $ lastIndexOf (Pattern "") "" == Just 0 --- assert $ lastIndexOf (Pattern "") "abcd" == Just 4 --- assert $ lastIndexOf (Pattern "bc") "abcd" == Just 1 --- assert $ lastIndexOf (Pattern "cb") "abcd" == Nothing --- --- log "lastIndexOf'" --- assert $ lastIndexOf' (Pattern "") 0 "" == Just 0 --- assert $ lastIndexOf' (Pattern "") (-1) "ab" == Nothing --- assert $ lastIndexOf' (Pattern "") 0 "ab" == Just 0 --- assert $ lastIndexOf' (Pattern "") 1 "ab" == Just 1 --- assert $ lastIndexOf' (Pattern "") 2 "ab" == Just 2 --- assert $ lastIndexOf' (Pattern "") 3 "ab" == Nothing --- assert $ lastIndexOf' (Pattern "bc") 0 "abcd" == Nothing --- assert $ lastIndexOf' (Pattern "bc") 1 "abcd" == Just 1 --- assert $ lastIndexOf' (Pattern "bc") 2 "abcd" == Just 1 --- assert $ lastIndexOf' (Pattern "cb") 0 "abcd" == Nothing --- --- log "length" --- assert $ length "" == 0 --- assert $ length "a" == 1 --- assert $ length "ab" == 2 --- --- log "take" --- assert $ take 0 "ab" == "" --- assert $ take 1 "ab" == "a" --- assert $ take 2 "ab" == "ab" --- assert $ take 3 "ab" == "ab" --- assert $ take (-1) "ab" == "" --- --- log "count" --- assert $ count (_ == 'a') "" == 0 --- assert $ count (_ == 'a') "ab" == 1 --- assert $ count (_ == 'a') "aaab" == 3 --- assert $ count (_ == 'a') "abaa" == 1 --- --- log "splitAt" --- 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 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 + log "indexOf'" + assert $ indexOf' (Pattern "") 0 "" == Just 0 + assert $ indexOf' (Pattern str) 0 str == Just 0 + assert $ indexOf' (Pattern str) 1 str == Nothing + assert $ indexOf' (Pattern "a") 0 str == Just 0 + assert $ indexOf' (Pattern "a") 1 str == Nothing + assert $ indexOf' (Pattern "z") 0 str == Just 6 + assert $ indexOf' (Pattern "z") 1 str == Just 6 + assert $ indexOf' (Pattern "z") 2 str == Just 6 + assert $ indexOf' (Pattern "z") 3 str == Just 6 + assert $ indexOf' (Pattern "z") 4 str == Just 6 + assert $ indexOf' (Pattern "z") 5 str == Just 6 + assert $ indexOf' (Pattern "z") 6 str == Just 6 + assert $ indexOf' (Pattern "z") 7 str == Nothing + + log "lastIndexOf" + assert $ lastIndexOf (Pattern "") "" == Just 0 + assert $ lastIndexOf (Pattern "") str == Just 7 + assert $ lastIndexOf (Pattern str) str == Just 0 + assert $ lastIndexOf (Pattern "a") str == Just 0 + assert $ lastIndexOf (Pattern "\xDC00\xD800\xD800") str == Just 1 + assert $ lastIndexOf (Pattern "\xD800") str == Just 3 + assert $ lastIndexOf (Pattern "\xD800\xD800") str == Just 2 + assert $ lastIndexOf (Pattern "\xD800\xD81A") str == Just 3 + assert $ lastIndexOf (Pattern "\xD800\x16805") str == Just 3 + assert $ lastIndexOf (Pattern "\x16805") str == Just 4 + assert $ lastIndexOf (Pattern "\x16A06") str == Just 5 + assert $ lastIndexOf (Pattern "z") str == Just 6 + assert $ lastIndexOf (Pattern "\0") str == Nothing + assert $ lastIndexOf (Pattern "\xD81A") str == Just 5 + + log "lastIndexOf'" + assert $ lastIndexOf' (Pattern "") 0 "" == Just 0 + assert $ lastIndexOf' (Pattern str) 0 str == Just 0 + assert $ lastIndexOf' (Pattern str) 1 str == Nothing + assert $ lastIndexOf' (Pattern "a") 0 str == Just 0 + assert $ lastIndexOf' (Pattern "a") 1 str == Nothing + assert $ lastIndexOf' (Pattern "z") 0 str == Just 6 + assert $ lastIndexOf' (Pattern "z") 1 str == Just 6 + assert $ lastIndexOf' (Pattern "z") 2 str == Just 6 + assert $ lastIndexOf' (Pattern "z") 3 str == Just 6 + assert $ lastIndexOf' (Pattern "z") 4 str == Just 6 + assert $ lastIndexOf' (Pattern "z") 5 str == Just 6 + assert $ lastIndexOf' (Pattern "z") 6 str == Just 6 + assert $ lastIndexOf' (Pattern "z") 7 str == Nothing + + log "length" + assert $ length "" == 0 + assert $ length "a" == 1 + assert $ length "ab" == 2 + assert $ length str == 7 + + log "singleton" + assert $ (singleton <$> codePointFromInt 0x30) == Just "0" + assert $ (singleton <$> codePointFromInt 0x16805) == Just "\x16805" + + log "splitAt" + let testSplitAt i s res = + 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 + + log "take" + assert $ take (-1) str == "" + assert $ take 0 str == "" + assert $ take 1 str == "a" + assert $ take 2 str == "a\xDC00" + assert $ take 3 str == "a\xDC00\xD800" + assert $ take 4 str == "a\xDC00\xD800\xD800" + assert $ take 5 str == "a\xDC00\xD800\xD800\x16805" + assert $ take 6 str == "a\xDC00\xD800\xD800\x16805\x16A06" + assert $ take 7 str == str + assert $ take 8 str == str + + log "takeWhile" + assert $ takeWhile (\c -> true) str == str + assert $ takeWhile (\c -> false) str == "" + assert $ takeWhile (\c -> codePointToInt c < 0xFFFF) str == "a\xDC00\xD800\xD800" + assert $ takeWhile (\c -> codePointToInt c < 0xDC00) str == "a" + + log "uncons" + let testUncons s res = + assert $ case uncons s of + Nothing -> + isNothing res + Just { head, tail } -> + maybe false (\r -> + r.head == codePointToInt head && r.tail == tail) res + + testUncons str $ Just {head: 0x61, tail: "\xDC00\xD800\xD800\x16805\x16A06\&z"} + testUncons (drop 1 str) $ Just {head: 0xDC00, tail: "\xD800\xD800\x16805\x16A06\&z"} + testUncons (drop 2 str) $ Just {head: 0xD800, tail: "\xD800\x16805\x16A06\&z"} + testUncons (drop 3 str) $ Just {head: 0xD800, tail: "\x16805\x16A06\&z"} + testUncons (drop 4 str) $ Just {head: 0x16805, tail: "\x16A06\&z"} + testUncons (drop 5 str) $ Just {head: 0x16A06, tail: "z"} + testUncons (drop 6 str) $ Just {head: 0x7A, tail: ""} + testUncons "" Nothing From 04154a5bee241c48f2a70f6417280e7f8ea587f4 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Sat, 27 May 2017 16:27:07 -0600 Subject: [PATCH 073/186] fix linting errors --- src/Data/String/CodePoints.js | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/Data/String/CodePoints.js b/src/Data/String/CodePoints.js index c1d9163..c1c982a 100644 --- a/src/Data/String/CodePoints.js +++ b/src/Data/String/CodePoints.js @@ -1,11 +1,21 @@ -var hasArrayFrom = typeof Array.from === 'function'; +"use strict"; +/* global Symbol */ + +var hasArrayFrom = typeof Array.from === "function"; var hasStringIterator = - typeof Symbol !== 'undefined' && + typeof Symbol !== "undefined" && Symbol != null && - typeof Symbol.iterator !== 'undefined' && - typeof String.prototype[Symbol.iterator] === 'function'; -var hasFromCodePoint = typeof String.prototype.fromCodePoint === 'function'; -var hasCodePointAt = typeof String.prototype.codePointAt === 'function'; + typeof Symbol.iterator !== "undefined" && + typeof String.prototype[Symbol.iterator] === "function"; +var hasFromCodePoint = typeof String.prototype.fromCodePoint === "function"; +var hasCodePointAt = typeof String.prototype.codePointAt === "function"; + +function fromCodePoint(cp) { + if (cp <= 0xFFFF) return String.fromCharCode(cp); + var cu1 = String.fromCharCode(Math.floor((cp - 0x10000) / 0x400) + 0xD800); + var cu2 = String.fromCharCode((cp - 0x10000) % 0x400 + 0xDC00); + return cu1 + cu2; +} exports._codePointAt = function (fallback) { return function (Just) { @@ -23,7 +33,7 @@ exports._codePointAt = function (fallback) { for (var i = index;; --i) { var o = iter.next(); if (o.done) return Nothing; - if (i == 0) return Just(o.value); + if (i === 0) return Just(o.value); } } return fallback(index)(str); @@ -61,7 +71,7 @@ exports._count = function (isLead) { exports.fromCodePointArray = hasFromCodePoint ? function (cps) { return String.fromCodePoint.apply(String, cps); } - : function (cps) { return cps.map(fromCodePoint).join(''); }; + : function (cps) { return cps.map(fromCodePoint).join(""); }; exports.singleton = hasFromCodePoint ? String.fromCodePoint : fromCodePoint; @@ -69,7 +79,7 @@ exports._take = function (fallback) { return function (n) { if (hasArrayFrom) { return function (str) { - return Array.from(str).slice(0, Math.max(0, n)).join(''); + return Array.from(str).slice(0, Math.max(0, n)).join(""); }; } else if (hasStringIterator) { return function (str) { @@ -105,10 +115,3 @@ exports._toCodePointArray = function (fallback) { } return fallback; }; - -function fromCodePoint(cp) { - if (cp <= 0xFFFF) return String.fromCharCode(cp); - var cu1 = String.fromCharCode(Math.floor((cp - 0x10000) / 0x400) + 0xD800); - var cu2 = String.fromCharCode((cp - 0x10000) % 0x400 + 0xDC00); - return cu1 + cu2; -} From c798dfe490651b219b980664b5a5462cfd2430cf Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Sat, 27 May 2017 16:46:59 -0600 Subject: [PATCH 074/186] change re-export of Data.String --- src/Data/String.purs | 4 ++-- src/Data/String/CodePoints.purs | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Data/String.purs b/src/Data/String.purs index 22be682..89ad23c 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -36,11 +36,11 @@ module Data.String , joinWith ) where -import Prelude +import Prelude (class Ord, class Eq, class Show, Ordering(LT, EQ, GT), zero, one, (<<<), (<>), (==), ($), (-)) import Data.Maybe (Maybe(..), isJust) import Data.Newtype (class Newtype) -import Data.String.Unsafe as U +import Data.String.Unsafe (charAt) as U -- | A newtype used in cases where there is a string to be matched. newtype Pattern = Pattern String diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index f6b49ed..c968adc 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -27,7 +27,8 @@ import Data.List (List(Cons, Nil), fromFoldable) import Data.Maybe (Maybe(Just, Nothing)) import Data.Newtype (class Newtype) import Data.String as String -import Data.String hiding (count, drop, dropWhile, indexOf, indexOf', lastIndexOf, lastIndexOf', length, singleton, splitAt, take, takeWhile, uncons) as StringReExports +-- WARN: This list must be updated to re-export any exports added to Data.String. That makes me sad. +import Data.String (Pattern(..), Replacement(..), charAt, charCodeAt, contains, fromCharArray, joinWith, localeCompare, null, replace, replaceAll, split, stripPrefix, stripSuffix, toChar, toCharArray, toLower, toUpper, trim) as StringReExports import Data.Tuple (Tuple(Tuple)) import Data.Unfoldable (unfoldr) import Prelude (class Eq, class Ord, (&&), (||), (*), (+), (-), (<$>), (<), (<=), (<<<)) From 71cdcf299d1acb32e702b4055fe65a721fa171b6 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Sun, 28 May 2017 14:45:12 -0600 Subject: [PATCH 075/186] bugfixes --- src/Data/String/CodePoints.js | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/Data/String/CodePoints.js b/src/Data/String/CodePoints.js index c1c982a..7c1257c 100644 --- a/src/Data/String/CodePoints.js +++ b/src/Data/String/CodePoints.js @@ -17,6 +17,15 @@ function fromCodePoint(cp) { return cu1 + cu2; } +var codePointAt0 = hasCodePointAt + ? function (str) { return str.codePointAt(0); } + : function (str) { + if (str.length === 1) { + return str.charCodeAt(0); + } + return ((str.charCodeAt(0) - 0xD800) * 0x400 + (str.charCodeAt(1) - 0xDC00) + 0x10000); + }; + exports._codePointAt = function (fallback) { return function (Just) { return function (Nothing) { @@ -24,16 +33,16 @@ exports._codePointAt = function (fallback) { return function (str) { var length = str.length; if (index < 0 || index >= length) return Nothing; - if (hasArrayFrom && hasCodePointAt) { + if (hasArrayFrom) { var cps = Array.from(str); if (index >= cps.length) return Nothing; - return Just(cps[index].codePointAt(0)); + return Just(codePointAt0(cps[index])); } else if (hasStringIterator) { var iter = str[Symbol.iterator](); for (var i = index;; --i) { var o = iter.next(); if (o.done) return Nothing; - if (i === 0) return Just(o.value); + if (i === 0) return Just(codePointAt0(o.value)); } } return fallback(index)(str); @@ -70,6 +79,7 @@ exports._count = function (isLead) { }; exports.fromCodePointArray = hasFromCodePoint + // TODO: using F.p.apply here will fail for very large strings; use alternative implementation for very large strings ? function (cps) { return String.fromCodePoint.apply(String, cps); } : function (cps) { return cps.map(fromCodePoint).join(""); }; @@ -93,23 +103,23 @@ exports._take = function (fallback) { return accum; }; } - return fallback; + return fallback(n); }; }; exports._toCodePointArray = function (fallback) { - if (hasArrayFrom && hasCodePointAt) { + if (hasArrayFrom) { return function (str) { - return Array.from(str, function (x) { return x.codePointAt(0); }); + return Array.from(str, codePointAt0); }; - } else if (hasStringIterator && hasCodePointAt) { + } else if (hasStringIterator) { return function (str) { var accum = []; var iter = str[Symbol.iterator](); for (;;) { var o = iter.next(); if (o.done) return accum; - accum.push(o.value.codePointAt(0)); + accum.push(codePointAt0(o.value)); } }; } From 2c2418aa41cf547b53c210361c06a1f62faaae1c Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Sun, 28 May 2017 15:33:18 -0600 Subject: [PATCH 076/186] move fromCodePoint from JS to purs --- src/Data/String/CodePoints.js | 21 +++++++++------------ src/Data/String/CodePoints.purs | 32 +++++++++++++++++++++++++++----- 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/Data/String/CodePoints.js b/src/Data/String/CodePoints.js index 7c1257c..2ae2481 100644 --- a/src/Data/String/CodePoints.js +++ b/src/Data/String/CodePoints.js @@ -10,13 +10,6 @@ var hasStringIterator = var hasFromCodePoint = typeof String.prototype.fromCodePoint === "function"; var hasCodePointAt = typeof String.prototype.codePointAt === "function"; -function fromCodePoint(cp) { - if (cp <= 0xFFFF) return String.fromCharCode(cp); - var cu1 = String.fromCharCode(Math.floor((cp - 0x10000) / 0x400) + 0xD800); - var cu2 = String.fromCharCode((cp - 0x10000) % 0x400 + 0xDC00); - return cu1 + cu2; -} - var codePointAt0 = hasCodePointAt ? function (str) { return str.codePointAt(0); } : function (str) { @@ -78,12 +71,16 @@ exports._count = function (isLead) { }; }; -exports.fromCodePointArray = hasFromCodePoint - // TODO: using F.p.apply here will fail for very large strings; use alternative implementation for very large strings - ? function (cps) { return String.fromCodePoint.apply(String, cps); } - : function (cps) { return cps.map(fromCodePoint).join(""); }; +exports._fromCodePointArray = function (singleton) { + return hasFromCodePoint + // TODO: using F.p.apply here will fail for very large strings; use alternative implementation for very large strings + ? function (cps) { return String.fromCodePoint.apply(String, cps); } + : function (cps) { return cps.map(singleton).join(""); }; +}; -exports.singleton = hasFromCodePoint ? String.fromCodePoint : fromCodePoint; +exports._singleton = function (fallback) { + return hasFromCodePoint ? String.fromCodePoint : fallback; +}; exports._take = function (fallback) { return function (n) { diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index c968adc..eaccf9d 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -22,7 +22,7 @@ module Data.String.CodePoints ) where import Data.Array as Array -import Data.Char (toCharCode) +import Data.Char as Char import Data.List (List(Cons, Nil), fromFoldable) import Data.Maybe (Maybe(Just, Nothing)) import Data.Newtype (class Newtype) @@ -31,7 +31,7 @@ import Data.String as String import Data.String (Pattern(..), Replacement(..), charAt, charCodeAt, contains, fromCharArray, joinWith, localeCompare, null, replace, replaceAll, split, stripPrefix, stripSuffix, toChar, toCharArray, toLower, toUpper, trim) as StringReExports import Data.Tuple (Tuple(Tuple)) import Data.Unfoldable (unfoldr) -import Prelude (class Eq, class Ord, (&&), (||), (*), (+), (-), (<$>), (<), (<=), (<<<)) +import Prelude (class Eq, class Ord, (&&), (||), (*), (+), (-), (<$>), (<), (<=), (<<<), (/), (<>), mod) newtype CodePoint = CodePoint Int @@ -61,6 +61,9 @@ isLead cu = 0xD800 <= cu && cu <= 0xDBFF isTrail :: Int -> Boolean isTrail cu = 0xDC00 <= cu && cu <= 0xDFFF +fromCharCode :: Int -> String +fromCharCode = String.singleton <<< Char.fromCharCode + codePointAt :: Int -> String -> Maybe CodePoint codePointAt = _codePointAt codePointAtFallback Just Nothing @@ -97,7 +100,13 @@ dropWhile :: (CodePoint -> Boolean) -> String -> String dropWhile p s = drop (count p s) s -foreign import fromCodePointArray :: Array CodePoint -> String +fromCodePointArray :: Array CodePoint -> String +fromCodePointArray = _fromCodePointArray singletonFallback + +foreign import _fromCodePointArray + :: (CodePoint -> String) + -> Array CodePoint + -> String indexOf :: String.Pattern -> String -> Maybe Int @@ -124,7 +133,20 @@ length :: String -> Int length = Array.length <<< toCodePointArray -foreign import singleton :: CodePoint -> String +singleton :: CodePoint -> String +singleton = _singleton singletonFallback + +foreign import _singleton + :: (CodePoint -> String) + -> CodePoint + -> String + +singletonFallback :: CodePoint -> String +singletonFallback (CodePoint cp) | cp <= 0xFFFF = fromCharCode cp +singletonFallback (CodePoint cp) = fromCharCode lead <> fromCharCode trail + where + lead = ((cp - 0x10000) / 0x400) + 0xD800 + trail = (cp - 0x10000) `mod` 0x400 + 0xDC00 splitAt :: Int -> String -> Maybe { before :: String, after :: String } @@ -160,7 +182,7 @@ foreign import _toCodePointArray -> Array CodePoint toCodePointArrayFallback :: String -> Array CodePoint -toCodePointArrayFallback s = unfoldr decode (fromFoldable (toCharCode <$> String.toCharArray s)) +toCodePointArrayFallback s = unfoldr decode (fromFoldable (Char.toCharCode <$> String.toCharArray s)) where decode :: List Int -> Maybe (Tuple CodePoint (List Int)) decode (Cons h (Cons l rest)) | isLead h && isTrail l From 46e9545c0d3d3c3168097131f770baf1e7e7995b Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Sun, 28 May 2017 15:54:08 -0600 Subject: [PATCH 077/186] move codePointAt0 from JS to purs --- src/Data/String/CodePoints.js | 85 ++++++++++++++++++--------------- src/Data/String/CodePoints.purs | 31 ++++++++++-- 2 files changed, 72 insertions(+), 44 deletions(-) diff --git a/src/Data/String/CodePoints.js b/src/Data/String/CodePoints.js index 2ae2481..58117af 100644 --- a/src/Data/String/CodePoints.js +++ b/src/Data/String/CodePoints.js @@ -10,35 +10,40 @@ var hasStringIterator = var hasFromCodePoint = typeof String.prototype.fromCodePoint === "function"; var hasCodePointAt = typeof String.prototype.codePointAt === "function"; -var codePointAt0 = hasCodePointAt - ? function (str) { return str.codePointAt(0); } - : function (str) { - if (str.length === 1) { - return str.charCodeAt(0); - } - return ((str.charCodeAt(0) - 0xD800) * 0x400 + (str.charCodeAt(1) - 0xDC00) + 0x10000); +exports._unsafeCharCodeAt = function (i) { + return function (str) { + return str.charCodeAt(i); }; +}; + +exports._unsafeCodePointAt0 = function (fallback) { + return hasCodePointAt + ? function (str) { return str.codePointAt(0); } + : fallback; +}; exports._codePointAt = function (fallback) { return function (Just) { return function (Nothing) { - return function (index) { - return function (str) { - var length = str.length; - if (index < 0 || index >= length) return Nothing; - if (hasArrayFrom) { - var cps = Array.from(str); - if (index >= cps.length) return Nothing; - return Just(codePointAt0(cps[index])); - } else if (hasStringIterator) { - var iter = str[Symbol.iterator](); - for (var i = index;; --i) { - var o = iter.next(); - if (o.done) return Nothing; - if (i === 0) return Just(codePointAt0(o.value)); + return function (unsafeCodePointAt0) { + return function (index) { + return function (str) { + var length = str.length; + if (index < 0 || index >= length) return Nothing; + if (hasArrayFrom) { + var cps = Array.from(str); + if (index >= cps.length) return Nothing; + return Just(unsafeCodePointAt0(cps[index])); + } else if (hasStringIterator) { + var iter = str[Symbol.iterator](); + for (var i = index;; --i) { + var o = iter.next(); + if (o.done) return Nothing; + if (i === 0) return Just(unsafeCodePointAt0(o.value)); + } } - } - return fallback(index)(str); + return fallback(index)(str); + }; }; }; }; @@ -105,20 +110,22 @@ exports._take = function (fallback) { }; exports._toCodePointArray = function (fallback) { - if (hasArrayFrom) { - return function (str) { - return Array.from(str, codePointAt0); - }; - } else if (hasStringIterator) { - return function (str) { - var accum = []; - var iter = str[Symbol.iterator](); - for (;;) { - var o = iter.next(); - if (o.done) return accum; - accum.push(codePointAt0(o.value)); - } - }; - } - return fallback; + return function (unsafeCodePointAt0) { + if (hasArrayFrom) { + return function (str) { + return Array.from(str, unsafeCodePointAt0); + }; + } else if (hasStringIterator) { + return function (str) { + var accum = []; + var iter = str[Symbol.iterator](); + for (;;) { + var o = iter.next(); + if (o.done) return accum; + accum.push(unsafeCodePointAt0(o.value)); + } + }; + } + return fallback; + }; }; diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index eaccf9d..ab14ccb 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -31,7 +31,7 @@ import Data.String as String import Data.String (Pattern(..), Replacement(..), charAt, charCodeAt, contains, fromCharArray, joinWith, localeCompare, null, replace, replaceAll, split, stripPrefix, stripSuffix, toChar, toCharArray, toLower, toUpper, trim) as StringReExports import Data.Tuple (Tuple(Tuple)) import Data.Unfoldable (unfoldr) -import Prelude (class Eq, class Ord, (&&), (||), (*), (+), (-), (<$>), (<), (<=), (<<<), (/), (<>), mod) +import Prelude (class Eq, class Ord, (&&), (||), (*), (+), (-), (<$>), (<), (<=), (<<<), (/), (<>), (==), mod) newtype CodePoint = CodePoint Int @@ -48,8 +48,8 @@ codePointToInt :: CodePoint -> Int codePointToInt (CodePoint n) = n codePointFromSurrogatePair :: Int -> Int -> Maybe CodePoint -codePointFromSurrogatePair lead trail | isLead lead && isTrail trail - = Just (unsurrogate lead trail) +codePointFromSurrogatePair lead trail | isLead lead && isTrail trail = + Just (unsurrogate lead trail) codePointFromSurrogatePair _ _ = Nothing unsurrogate :: Int -> Int -> CodePoint @@ -64,14 +64,34 @@ isTrail cu = 0xDC00 <= cu && cu <= 0xDFFF fromCharCode :: Int -> String fromCharCode = String.singleton <<< Char.fromCharCode +unsafeCodePointAt0 :: String -> CodePoint +unsafeCodePointAt0 = _unsafeCodePointAt0 unsafeCodePointAt0Fallback + +foreign import _unsafeCodePointAt0 + :: (String -> CodePoint) + -> String + -> CodePoint + +unsafeCodePointAt0Fallback :: String -> CodePoint +unsafeCodePointAt0Fallback s | String.length s == 1 = CodePoint (_unsafeCharCodeAt 0 s) +unsafeCodePointAt0Fallback s = CodePoint (((lead - 0xD800) * 0x400) + (trail - 0xDC00) + 0x10000) + where + lead = _unsafeCharCodeAt 0 s + trail = _unsafeCharCodeAt 1 s + +foreign import _unsafeCharCodeAt :: Int -> String -> Int + codePointAt :: Int -> String -> Maybe CodePoint -codePointAt = _codePointAt codePointAtFallback Just Nothing +codePointAt 0 "" = Nothing +codePointAt 0 s = Just (unsafeCodePointAt0 s) +codePointAt n s = _codePointAt codePointAtFallback Just Nothing unsafeCodePointAt0 n s foreign import _codePointAt :: (Int -> String -> Maybe CodePoint) -> (forall a. a -> Maybe a) -> (forall a. Maybe a) + -> (String -> CodePoint) -> Int -> String -> Maybe CodePoint @@ -174,10 +194,11 @@ takeWhile p s = take (count p s) s toCodePointArray :: String -> Array CodePoint -toCodePointArray = _toCodePointArray toCodePointArrayFallback +toCodePointArray = _toCodePointArray toCodePointArrayFallback unsafeCodePointAt0 foreign import _toCodePointArray :: (String -> Array CodePoint) + -> (String -> CodePoint) -> String -> Array CodePoint From c59f34028a1cd7ea9de96d732c938d27e5ea3183 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Mon, 29 May 2017 11:30:46 -0700 Subject: [PATCH 078/186] remove TODOs --- src/Data/String/CodePoints.js | 8 ++++++-- test/Test/Data/String/CodePoints.purs | 5 ----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Data/String/CodePoints.js b/src/Data/String/CodePoints.js index 58117af..1875fab 100644 --- a/src/Data/String/CodePoints.js +++ b/src/Data/String/CodePoints.js @@ -78,8 +78,12 @@ exports._count = function (isLead) { exports._fromCodePointArray = function (singleton) { return hasFromCodePoint - // TODO: using F.p.apply here will fail for very large strings; use alternative implementation for very large strings - ? function (cps) { return String.fromCodePoint.apply(String, cps); } + ? function (cps) { + if (cps.length < 10240) { + return String.fromCodePoint.apply(String, cps); + } + return cps.map(singleton).join(""); + } : function (cps) { return cps.map(singleton).join(""); }; }; diff --git a/test/Test/Data/String/CodePoints.purs b/test/Test/Data/String/CodePoints.purs index d7dac6a..46dcc9f 100644 --- a/test/Test/Data/String/CodePoints.purs +++ b/test/Test/Data/String/CodePoints.purs @@ -66,11 +66,6 @@ testStringCodePoints = do assert $ indexOf (Pattern "z") str == Just 6 assert $ indexOf (Pattern "\0") str == Nothing assert $ indexOf (Pattern "\xD81A") str == Just 4 - -- TODO: Should this be Nothing? It matches the trail surrogate of a surrogate pair. - -- It'd be nice if (drop (indexOf pattern str) str) was guaranteed to start with pattern. - -- If we change this, we'll also need to add a matching contains implementation to the CodePoints module. - -- I vote we just delete the test. Passing surrogate halves to the CodePoints functions should not be supported. - assert $ indexOf (Pattern "\xDC05") str == Just 5 log "indexOf'" assert $ indexOf' (Pattern "") 0 "" == Just 0 From 71c5156e74a5569d81c9b43906dbc111e25e4c36 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Wed, 31 May 2017 20:56:23 -0700 Subject: [PATCH 079/186] use charCodeAt from Data.String.Unsafe --- src/Data/String/CodePoints.js | 6 ------ src/Data/String/CodePoints.purs | 9 ++++----- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/Data/String/CodePoints.js b/src/Data/String/CodePoints.js index 1875fab..1a9b534 100644 --- a/src/Data/String/CodePoints.js +++ b/src/Data/String/CodePoints.js @@ -10,12 +10,6 @@ var hasStringIterator = var hasFromCodePoint = typeof String.prototype.fromCodePoint === "function"; var hasCodePointAt = typeof String.prototype.codePointAt === "function"; -exports._unsafeCharCodeAt = function (i) { - return function (str) { - return str.charCodeAt(i); - }; -}; - exports._unsafeCodePointAt0 = function (fallback) { return hasCodePointAt ? function (str) { return str.codePointAt(0); } diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index ab14ccb..2ab7f3f 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -27,6 +27,7 @@ import Data.List (List(Cons, Nil), fromFoldable) import Data.Maybe (Maybe(Just, Nothing)) import Data.Newtype (class Newtype) import Data.String as String +import Data.String.Unsafe as Unsafe -- WARN: This list must be updated to re-export any exports added to Data.String. That makes me sad. import Data.String (Pattern(..), Replacement(..), charAt, charCodeAt, contains, fromCharArray, joinWith, localeCompare, null, replace, replaceAll, split, stripPrefix, stripSuffix, toChar, toCharArray, toLower, toUpper, trim) as StringReExports import Data.Tuple (Tuple(Tuple)) @@ -73,13 +74,11 @@ foreign import _unsafeCodePointAt0 -> CodePoint unsafeCodePointAt0Fallback :: String -> CodePoint -unsafeCodePointAt0Fallback s | String.length s == 1 = CodePoint (_unsafeCharCodeAt 0 s) +unsafeCodePointAt0Fallback s | String.length s == 1 = CodePoint (Unsafe.charCodeAt 0 s) unsafeCodePointAt0Fallback s = CodePoint (((lead - 0xD800) * 0x400) + (trail - 0xDC00) + 0x10000) where - lead = _unsafeCharCodeAt 0 s - trail = _unsafeCharCodeAt 1 s - -foreign import _unsafeCharCodeAt :: Int -> String -> Int + lead = Unsafe.charCodeAt 0 s + trail = Unsafe.charCodeAt 1 s codePointAt :: Int -> String -> Maybe CodePoint From e8ca6f36278fcce565d83a313f4e607ffb2b52bc Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Sun, 4 Jun 2017 21:32:23 -0700 Subject: [PATCH 080/186] open imports for Prelude --- src/Data/String.purs | 2 +- src/Data/String/CodePoints.purs | 3 ++- test/Test/Data/String/CodePoints.purs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Data/String.purs b/src/Data/String.purs index 89ad23c..2bd2c94 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -36,7 +36,7 @@ module Data.String , joinWith ) where -import Prelude (class Ord, class Eq, class Show, Ordering(LT, EQ, GT), zero, one, (<<<), (<>), (==), ($), (-)) +import Prelude import Data.Maybe (Maybe(..), isJust) import Data.Newtype (class Newtype) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 2ab7f3f..71ffa85 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -21,6 +21,8 @@ module Data.String.CodePoints , uncons ) where +import Prelude + import Data.Array as Array import Data.Char as Char import Data.List (List(Cons, Nil), fromFoldable) @@ -32,7 +34,6 @@ import Data.String.Unsafe as Unsafe import Data.String (Pattern(..), Replacement(..), charAt, charCodeAt, contains, fromCharArray, joinWith, localeCompare, null, replace, replaceAll, split, stripPrefix, stripSuffix, toChar, toCharArray, toLower, toUpper, trim) as StringReExports import Data.Tuple (Tuple(Tuple)) import Data.Unfoldable (unfoldr) -import Prelude (class Eq, class Ord, (&&), (||), (*), (+), (-), (<$>), (<), (<=), (<<<), (/), (<>), (==), mod) newtype CodePoint = CodePoint Int diff --git a/test/Test/Data/String/CodePoints.purs b/test/Test/Data/String/CodePoints.purs index 46dcc9f..f6844cd 100644 --- a/test/Test/Data/String/CodePoints.purs +++ b/test/Test/Data/String/CodePoints.purs @@ -1,6 +1,6 @@ module Test.Data.String.CodePoints (testStringCodePoints) where -import Prelude (Unit, discard, negate, (==), ($), (&&), (<), (<$>)) +import Prelude import Control.Monad.Eff (Eff) import Control.Monad.Eff.Console (CONSOLE, log) From 8d6d263bac2ddae7c605bda329adf55854cf10e2 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Mon, 5 Jun 2017 00:09:52 -0700 Subject: [PATCH 081/186] add some comments --- src/Data/String/CodePoints.js | 2 ++ src/Data/String/CodePoints.purs | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/src/Data/String/CodePoints.js b/src/Data/String/CodePoints.js index 1a9b534..34b7aa6 100644 --- a/src/Data/String/CodePoints.js +++ b/src/Data/String/CodePoints.js @@ -73,6 +73,8 @@ exports._count = function (isLead) { exports._fromCodePointArray = function (singleton) { return hasFromCodePoint ? function (cps) { + // Function.prototype.apply will fail for very large second parameters, + // so we don't use it for arrays with 10KB or more entries. if (cps.length < 10240) { return String.fromCodePoint.apply(String, cps); } diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 71ffa85..dbc4294 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -1,3 +1,7 @@ +-- | These functions allow PureScript strings to be treated as if they were +-- | sequences of Unicode code points instead of their true underlying +-- | implementation (sequences of UTF-16 code units). For nearly all uses of +-- | strings, these functions should be preferred over the ones in Data.String. module Data.String.CodePoints ( module StringReExports , CodePoint() @@ -36,12 +40,17 @@ import Data.Tuple (Tuple(Tuple)) import Data.Unfoldable (unfoldr) +-- | CodePoint is an Int bounded between 0 and 0x10FFFF, corresponding to +-- | Unicode code points. newtype CodePoint = CodePoint Int derive instance eqCodePoint :: Eq CodePoint derive instance ordCodePoint :: Ord CodePoint derive instance newtypeCodePoint :: Newtype CodePoint _ +-- I would prefer that this smart constructor not need to exist and instead +-- CodePoint just implements Enum, but the Enum module already depends on this +-- one. To avoid the circular dependency, we just expose these two functions. codePointFromInt :: Int -> Maybe CodePoint codePointFromInt n | 0 <= n && n <= 0x10FFFF = Just (CodePoint n) codePointFromInt n = Nothing From 8e99c3996dcfe5349b73a7b7d87f1237cb3068ae Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Mon, 5 Jun 2017 00:11:23 -0700 Subject: [PATCH 082/186] remove unused parameters --- test/Test/Data/String/CodePoints.purs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/Test/Data/String/CodePoints.purs b/test/Test/Data/String/CodePoints.purs index f6844cd..457ba80 100644 --- a/test/Test/Data/String/CodePoints.purs +++ b/test/Test/Data/String/CodePoints.purs @@ -46,8 +46,8 @@ testStringCodePoints = do assert $ drop 8 str == "" log "dropWhile" - assert $ dropWhile (\c -> true) str == "" - assert $ dropWhile (\c -> false) str == str + assert $ dropWhile (\_ -> true) str == "" + assert $ dropWhile (\_ -> false) str == str assert $ dropWhile (\c -> codePointToInt c < 0xFFFF) str == "\x16805\x16A06\&z" assert $ dropWhile (\c -> codePointToInt c < 0xDC00) str == "\xDC00\xD800\xD800\x16805\x16A06\&z" @@ -161,8 +161,8 @@ testStringCodePoints = do assert $ take 8 str == str log "takeWhile" - assert $ takeWhile (\c -> true) str == str - assert $ takeWhile (\c -> false) str == "" + assert $ takeWhile (\_ -> true) str == str + assert $ takeWhile (\_ -> false) str == "" assert $ takeWhile (\c -> codePointToInt c < 0xFFFF) str == "a\xDC00\xD800\xD800" assert $ takeWhile (\c -> codePointToInt c < 0xDC00) str == "a" From 557186c275894defa4a74d6ff2aa0dc09ad5b448 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Mon, 5 Jun 2017 00:17:54 -0700 Subject: [PATCH 083/186] remove some redundant JS implementations --- src/Data/String/CodePoints.js | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/Data/String/CodePoints.js b/src/Data/String/CodePoints.js index 34b7aa6..aefe6a6 100644 --- a/src/Data/String/CodePoints.js +++ b/src/Data/String/CodePoints.js @@ -80,7 +80,9 @@ exports._fromCodePointArray = function (singleton) { } return cps.map(singleton).join(""); } - : function (cps) { return cps.map(singleton).join(""); }; + : function (cps) { + return cps.map(singleton).join(""); + }; }; exports._singleton = function (fallback) { @@ -89,11 +91,7 @@ exports._singleton = function (fallback) { exports._take = function (fallback) { return function (n) { - if (hasArrayFrom) { - return function (str) { - return Array.from(str).slice(0, Math.max(0, n)).join(""); - }; - } else if (hasStringIterator) { + if (hasStringIterator) { return function (str) { var accum = ""; var iter = str[Symbol.iterator](); @@ -115,16 +113,6 @@ exports._toCodePointArray = function (fallback) { return function (str) { return Array.from(str, unsafeCodePointAt0); }; - } else if (hasStringIterator) { - return function (str) { - var accum = []; - var iter = str[Symbol.iterator](); - for (;;) { - var o = iter.next(); - if (o.done) return accum; - accum.push(unsafeCodePointAt0(o.value)); - } - }; } return fallback; }; From 4ec116b76cc856bac1d990632836b16bbfcf6246 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Mon, 5 Jun 2017 00:32:13 -0700 Subject: [PATCH 084/186] remove unnecessary qualification in import --- src/Data/String.purs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Data/String.purs b/src/Data/String.purs index 2bd2c94..22be682 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -40,7 +40,7 @@ import Prelude import Data.Maybe (Maybe(..), isJust) import Data.Newtype (class Newtype) -import Data.String.Unsafe (charAt) as U +import Data.String.Unsafe as U -- | A newtype used in cases where there is a string to be matched. newtype Pattern = Pattern String From 3d400a47ab217a23295a0e9ec60a2e6a8be872f3 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Mon, 5 Jun 2017 16:46:53 +0100 Subject: [PATCH 085/186] Vary the length of generated strings --- src/Data/String/Gen.purs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Data/String/Gen.purs b/src/Data/String/Gen.purs index 0521711..6d2a762 100644 --- a/src/Data/String/Gen.purs +++ b/src/Data/String/Gen.purs @@ -2,14 +2,16 @@ module Data.String.Gen where import Prelude -import Control.Monad.Gen (class MonadGen, unfoldable) +import Control.Monad.Gen (class MonadGen, chooseInt, unfoldable, sized, resize) import Control.Monad.Rec.Class (class MonadRec) import Data.Char.Gen as CG import Data.String as S -- | Generates a string using the specified character generator. genString :: forall m. MonadRec m => MonadGen m => m Char -> m String -genString = map S.fromCharArray <<< unfoldable +genString genChar = sized \size -> do + newSize <- chooseInt 1 size + resize (const newSize) $ S.fromCharArray <$> unfoldable genChar -- | Generates a string using characters from the Unicode basic multilingual -- | plain. From fd7ca545994b0391dd3fecf8693916d830c8b7c0 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Mon, 5 Jun 2017 16:48:38 +0100 Subject: [PATCH 086/186] Add some additional common character generators --- src/Data/Char/Gen.purs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Data/Char/Gen.purs b/src/Data/Char/Gen.purs index cab95a0..d06ddb0 100644 --- a/src/Data/Char/Gen.purs +++ b/src/Data/Char/Gen.purs @@ -2,8 +2,9 @@ module Data.Char.Gen where import Prelude -import Control.Monad.Gen (class MonadGen, chooseInt) +import Control.Monad.Gen (class MonadGen, chooseInt, oneOf) import Data.Char as C +import Data.NonEmpty ((:|)) -- | Generates a character of the Unicode basic multilingual plain. genUnicodeChar :: forall m. MonadGen m => m Char @@ -20,3 +21,15 @@ genAsciiChar' = C.fromCharCode <$> chooseInt 0 127 -- | Generates a character that is a numeric digit. genDigitChar :: forall m. MonadGen m => m Char genDigitChar = C.fromCharCode <$> chooseInt 48 57 + +-- | Generates a character from the basic latin alphabet. +genAlpha :: forall m. MonadGen m => m Char +genAlpha = oneOf (genAlphaLowercase :| [genAlphaUppercase]) + +-- | Generates a lowercase character from the basic latin alphabet. +genAlphaLowercase :: forall m. MonadGen m => m Char +genAlphaLowercase = C.fromCharCode <$> chooseInt 97 122 + +-- | Generates an uppercase character from the basic latin alphabet. +genAlphaUppercase :: forall m. MonadGen m => m Char +genAlphaUppercase = C.fromCharCode <$> chooseInt 65 90 From 3c844982b2f0c0fc35681d61a6e72de1d58a27bc Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Tue, 6 Jun 2017 13:20:39 +0100 Subject: [PATCH 087/186] Ensure `genString` always generates at least one character --- src/Data/String/Gen.purs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Data/String/Gen.purs b/src/Data/String/Gen.purs index 6d2a762..e981a11 100644 --- a/src/Data/String/Gen.purs +++ b/src/Data/String/Gen.purs @@ -10,7 +10,7 @@ import Data.String as S -- | Generates a string using the specified character generator. genString :: forall m. MonadRec m => MonadGen m => m Char -> m String genString genChar = sized \size -> do - newSize <- chooseInt 1 size + newSize <- chooseInt 1 (max 1 size) resize (const newSize) $ S.fromCharArray <$> unfoldable genChar -- | Generates a string using characters from the Unicode basic multilingual From c680ede5500fd8a7332b6d089be2bbe4ee5acaf0 Mon Sep 17 00:00:00 2001 From: Daan Rijks Date: Mon, 12 Jun 2017 17:15:30 +0200 Subject: [PATCH 088/186] Fix typo: 'plain' -> 'plane' https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane --- src/Data/Char/Gen.purs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Data/Char/Gen.purs b/src/Data/Char/Gen.purs index d06ddb0..313cab3 100644 --- a/src/Data/Char/Gen.purs +++ b/src/Data/Char/Gen.purs @@ -6,7 +6,7 @@ import Control.Monad.Gen (class MonadGen, chooseInt, oneOf) import Data.Char as C import Data.NonEmpty ((:|)) --- | Generates a character of the Unicode basic multilingual plain. +-- | Generates a character of the Unicode basic multilingual plane. genUnicodeChar :: forall m. MonadGen m => m Char genUnicodeChar = C.fromCharCode <$> chooseInt 0 65536 From 5490d4679e4ce9e8ad4b52a3ad37bebeaa389474 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Mon, 12 Jun 2017 09:48:01 -0700 Subject: [PATCH 089/186] prefer 10e3 over 1024e1 --- src/Data/String/CodePoints.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Data/String/CodePoints.js b/src/Data/String/CodePoints.js index aefe6a6..87fb2c5 100644 --- a/src/Data/String/CodePoints.js +++ b/src/Data/String/CodePoints.js @@ -74,8 +74,8 @@ exports._fromCodePointArray = function (singleton) { return hasFromCodePoint ? function (cps) { // Function.prototype.apply will fail for very large second parameters, - // so we don't use it for arrays with 10KB or more entries. - if (cps.length < 10240) { + // so we don't use it for arrays with 10,000 or more entries. + if (cps.length < 10e3) { return String.fromCodePoint.apply(String, cps); } return cps.map(singleton).join(""); From af2db11877de7ecac511ed35c9677560540bfa4b Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Fri, 23 Jun 2017 08:42:37 -0700 Subject: [PATCH 090/186] prefer string iteration over Array.from in _codePointAt FFI function --- src/Data/String/CodePoints.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Data/String/CodePoints.js b/src/Data/String/CodePoints.js index 87fb2c5..041e275 100644 --- a/src/Data/String/CodePoints.js +++ b/src/Data/String/CodePoints.js @@ -24,17 +24,17 @@ exports._codePointAt = function (fallback) { return function (str) { var length = str.length; if (index < 0 || index >= length) return Nothing; - if (hasArrayFrom) { - var cps = Array.from(str); - if (index >= cps.length) return Nothing; - return Just(unsafeCodePointAt0(cps[index])); - } else if (hasStringIterator) { + if (hasStringIterator) { var iter = str[Symbol.iterator](); for (var i = index;; --i) { var o = iter.next(); if (o.done) return Nothing; if (i === 0) return Just(unsafeCodePointAt0(o.value)); } + } else if (hasArrayFrom) { + var cps = Array.from(str); + if (index >= cps.length) return Nothing; + return Just(unsafeCodePointAt0(cps[index])); } return fallback(index)(str); }; From 205838c84190ec300c6d8d1bca6ec1a3fd9990c6 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Tue, 4 Jul 2017 11:48:26 -0700 Subject: [PATCH 091/186] remove Newtype instance for CodePoint --- src/Data/String/CodePoints.purs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index dbc4294..10b3542 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -31,7 +31,6 @@ import Data.Array as Array import Data.Char as Char import Data.List (List(Cons, Nil), fromFoldable) import Data.Maybe (Maybe(Just, Nothing)) -import Data.Newtype (class Newtype) import Data.String as String import Data.String.Unsafe as Unsafe -- WARN: This list must be updated to re-export any exports added to Data.String. That makes me sad. @@ -46,7 +45,6 @@ newtype CodePoint = CodePoint Int derive instance eqCodePoint :: Eq CodePoint derive instance ordCodePoint :: Ord CodePoint -derive instance newtypeCodePoint :: Newtype CodePoint _ -- I would prefer that this smart constructor not need to exist and instead -- CodePoint just implements Enum, but the Enum module already depends on this From 3b57fd4fa775fbb9baf65471e5b03e3ab572ecfc Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Tue, 4 Jul 2017 12:48:06 -0700 Subject: [PATCH 092/186] remove duplication --- src/Data/String/CodePoints.purs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 10b3542..fb926dd 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -62,7 +62,7 @@ codePointFromSurrogatePair lead trail | isLead lead && isTrail trail = codePointFromSurrogatePair _ _ = Nothing unsurrogate :: Int -> Int -> CodePoint -unsurrogate h l = CodePoint ((h - 0xD800) * 0x400 + (l - 0xDC00) + 0x10000) +unsurrogate lead trail = CodePoint ((lead - 0xD800) * 0x400 + (trail - 0xDC00) + 0x10000) isLead :: Int -> Boolean isLead cu = 0xD800 <= cu && cu <= 0xDBFF @@ -83,7 +83,7 @@ foreign import _unsafeCodePointAt0 unsafeCodePointAt0Fallback :: String -> CodePoint unsafeCodePointAt0Fallback s | String.length s == 1 = CodePoint (Unsafe.charCodeAt 0 s) -unsafeCodePointAt0Fallback s = CodePoint (((lead - 0xD800) * 0x400) + (trail - 0xDC00) + 0x10000) +unsafeCodePointAt0Fallback s = CodePoint (unsurrogate lead trail) where lead = Unsafe.charCodeAt 0 s trail = Unsafe.charCodeAt 1 s From 7eac69e2efcd59d17a8bfcdf570954fc3bdd5f3b Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Tue, 4 Jul 2017 12:50:57 -0700 Subject: [PATCH 093/186] remove unused function --- src/Data/String/CodePoints.purs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index fb926dd..acca533 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -56,11 +56,6 @@ codePointFromInt n = Nothing codePointToInt :: CodePoint -> Int codePointToInt (CodePoint n) = n -codePointFromSurrogatePair :: Int -> Int -> Maybe CodePoint -codePointFromSurrogatePair lead trail | isLead lead && isTrail trail = - Just (unsurrogate lead trail) -codePointFromSurrogatePair _ _ = Nothing - unsurrogate :: Int -> Int -> CodePoint unsurrogate lead trail = CodePoint ((lead - 0xD800) * 0x400 + (trail - 0xDC00) + 0x10000) From cde0d26213ceec0b9083eb04709513f278bf5579 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Wed, 5 Jul 2017 09:49:31 -0700 Subject: [PATCH 094/186] bug fix for unsafeCodePointAt0Fallback --- src/Data/String/CodePoints.purs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index acca533..ed0bff5 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -77,11 +77,13 @@ foreign import _unsafeCodePointAt0 -> CodePoint unsafeCodePointAt0Fallback :: String -> CodePoint -unsafeCodePointAt0Fallback s | String.length s == 1 = CodePoint (Unsafe.charCodeAt 0 s) -unsafeCodePointAt0Fallback s = CodePoint (unsurrogate lead trail) +unsafeCodePointAt0Fallback s = + if isLead cu0 && isTrail cu1 + then unsurrogate cu0 cu1 + else CodePoint cu0 where - lead = Unsafe.charCodeAt 0 s - trail = Unsafe.charCodeAt 1 s + cu0 = Unsafe.charCodeAt 0 s + cu1 = Unsafe.charCodeAt 1 s codePointAt :: Int -> String -> Maybe CodePoint From 4292a8bad00ff9b0ea896268cf4b5a7f489f2d01 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Wed, 5 Jul 2017 09:51:48 -0700 Subject: [PATCH 095/186] consistent code unit variable names --- src/Data/String/CodePoints.purs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index ed0bff5..8ceb5d7 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -210,9 +210,9 @@ toCodePointArrayFallback :: String -> Array CodePoint toCodePointArrayFallback s = unfoldr decode (fromFoldable (Char.toCharCode <$> String.toCharArray s)) where decode :: List Int -> Maybe (Tuple CodePoint (List Int)) - decode (Cons h (Cons l rest)) | isLead h && isTrail l - = Just (Tuple (unsurrogate h l) rest) - decode (Cons c rest) = Just (Tuple (CodePoint c) rest) + decode (Cons cu0 (Cons cu1 rest)) | isLead cu0 && isTrail cu1 + = Just (Tuple (unsurrogate cu0 cu1) rest) + decode (Cons cu rest) = Just (Tuple (CodePoint cu) rest) decode Nil = Nothing From 0d81e0b4207a37f0173d2864d925f70052250d28 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Thu, 6 Jul 2017 09:45:28 -0700 Subject: [PATCH 096/186] bug fix lastIndexOf' --- src/Data/String/CodePoints.purs | 4 ++-- test/Test/Data/String/CodePoints.purs | 31 +++++++++++++++++++-------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 8ceb5d7..e7bb424 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -149,8 +149,8 @@ lastIndexOf p s = (\i -> length (String.take i s)) <$> String.lastIndexOf p s lastIndexOf' :: String.Pattern -> Int -> String -> Maybe Int lastIndexOf' p i s = - let s' = drop i s in - (\k -> i + length (String.take k s')) <$> String.lastIndexOf p s' + let i' = String.length (take i s) in + (\k -> length (String.take k s)) <$> String.lastIndexOf' p i' s length :: String -> Int diff --git a/test/Test/Data/String/CodePoints.purs b/test/Test/Data/String/CodePoints.purs index 457ba80..7a6aef0 100644 --- a/test/Test/Data/String/CodePoints.purs +++ b/test/Test/Data/String/CodePoints.purs @@ -101,17 +101,30 @@ testStringCodePoints = do log "lastIndexOf'" assert $ lastIndexOf' (Pattern "") 0 "" == Just 0 assert $ lastIndexOf' (Pattern str) 0 str == Just 0 - assert $ lastIndexOf' (Pattern str) 1 str == Nothing + assert $ lastIndexOf' (Pattern str) 1 str == Just 0 assert $ lastIndexOf' (Pattern "a") 0 str == Just 0 - assert $ lastIndexOf' (Pattern "a") 1 str == Nothing - assert $ lastIndexOf' (Pattern "z") 0 str == Just 6 - assert $ lastIndexOf' (Pattern "z") 1 str == Just 6 - assert $ lastIndexOf' (Pattern "z") 2 str == Just 6 - assert $ lastIndexOf' (Pattern "z") 3 str == Just 6 - assert $ lastIndexOf' (Pattern "z") 4 str == Just 6 - assert $ lastIndexOf' (Pattern "z") 5 str == Just 6 + assert $ lastIndexOf' (Pattern "a") 7 str == Just 0 + assert $ lastIndexOf' (Pattern "z") 0 str == Nothing + assert $ lastIndexOf' (Pattern "z") 1 str == Nothing + assert $ lastIndexOf' (Pattern "z") 2 str == Nothing + assert $ lastIndexOf' (Pattern "z") 3 str == Nothing + assert $ lastIndexOf' (Pattern "z") 4 str == Nothing + assert $ lastIndexOf' (Pattern "z") 5 str == Nothing assert $ lastIndexOf' (Pattern "z") 6 str == Just 6 - assert $ lastIndexOf' (Pattern "z") 7 str == Nothing + assert $ lastIndexOf' (Pattern "z") 7 str == Just 6 + assert $ lastIndexOf' (Pattern "\xD800") 7 str == Just 3 + assert $ lastIndexOf' (Pattern "\xD800") 6 str == Just 3 + assert $ lastIndexOf' (Pattern "\xD800") 5 str == Just 3 + assert $ lastIndexOf' (Pattern "\xD800") 4 str == Just 3 + assert $ lastIndexOf' (Pattern "\xD800") 3 str == Just 3 + assert $ lastIndexOf' (Pattern "\xD800") 2 str == Just 2 + assert $ lastIndexOf' (Pattern "\xD800") 1 str == Nothing + assert $ lastIndexOf' (Pattern "\xD800") 0 str == Nothing + assert $ lastIndexOf' (Pattern "\x16A06") 7 str == Just 5 + assert $ lastIndexOf' (Pattern "\x16A06") 6 str == Just 5 + assert $ lastIndexOf' (Pattern "\x16A06") 5 str == Just 5 + assert $ lastIndexOf' (Pattern "\x16A06") 4 str == Nothing + assert $ lastIndexOf' (Pattern "\x16A06") 3 str == Nothing log "length" assert $ length "" == 0 From 370af7cae24407f339d1f9e1fdbcd0eabd7d27e2 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Thu, 6 Jul 2017 10:07:13 -0700 Subject: [PATCH 097/186] add comments and complexity notes --- src/Data/String/CodePoints.purs | 46 ++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index e7bb424..31b7747 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -68,6 +68,7 @@ isTrail cu = 0xDC00 <= cu && cu <= 0xDFFF fromCharCode :: Int -> String fromCharCode = String.singleton <<< Char.fromCharCode +-- WARN: this function expects the String parameter to be non-empty unsafeCodePointAt0 :: String -> CodePoint unsafeCodePointAt0 = _unsafeCodePointAt0 unsafeCodePointAt0Fallback @@ -86,6 +87,9 @@ unsafeCodePointAt0Fallback s = cu1 = Unsafe.charCodeAt 1 s +-- | Returns the first code point of the string after dropping the given number +-- | of code points from the beginning, if there is such a code point. Operates +-- | in constant space and in time linear to `n`. codePointAt :: Int -> String -> Maybe CodePoint codePointAt 0 "" = Nothing codePointAt 0 s = Just (unsafeCodePointAt0 s) @@ -104,6 +108,9 @@ codePointAtFallback :: Int -> String -> Maybe CodePoint codePointAtFallback n s = Array.index (toCodePointArray s) n +-- | Returns the number of code points in the leading sequence of code points +-- | which all match the given predicate. Operates in constant space and in +-- | time linear to the length of the given string. count :: (CodePoint -> Boolean) -> String -> Int count = _count isLead isTrail unsurrogate @@ -116,14 +123,23 @@ foreign import _count -> Int +-- | Drops the given number of code points from the beginning of the given +-- | string. If the string does not have that many code points, returns the +-- | empty string. Operates in space and time linear to the length of the given +-- | string. drop :: Int -> String -> String drop n s = fromCodePointArray (Array.drop n (toCodePointArray s)) +-- | Drops the leading sequence of code points which all match the given +-- | predicate from the given string. Operates in space and time linear to the +-- | length of the given string. dropWhile :: (CodePoint -> Boolean) -> String -> String dropWhile p s = drop (count p s) s +-- | Creates a string from an array of code points. Operates in space and time +-- | linear to the length of the given array. fromCodePointArray :: Array CodePoint -> String fromCodePointArray = _fromCodePointArray singletonFallback @@ -132,31 +148,44 @@ foreign import _fromCodePointArray -> Array CodePoint -> String - +-- | Returns the number of code points preceding the first match of the given +-- | pattern in the given string. Returns Nothing when no matches are found. indexOf :: String.Pattern -> String -> Maybe Int indexOf p s = (\i -> length (String.take i s)) <$> String.indexOf p s +-- | Returns the number of code points preceding the first match of the given +-- | pattern in the given string. Pattern matches preceding the given index +-- | will be ignored. Returns Nothing when no matches are found. indexOf' :: String.Pattern -> Int -> String -> Maybe Int indexOf' p i s = let s' = drop i s in (\k -> i + length (String.take k s')) <$> String.indexOf p s' +-- | Returns the number of code points preceding the last match of the given +-- | pattern in the given string. Returns Nothing when no matches are found. lastIndexOf :: String.Pattern -> String -> Maybe Int lastIndexOf p s = (\i -> length (String.take i s)) <$> String.lastIndexOf p s +-- | Returns the number of code points preceding the first match of the given +-- | pattern in the given string. Pattern matches following the given index +-- | will be ignored. Returns Nothing when no matches are found. lastIndexOf' :: String.Pattern -> Int -> String -> Maybe Int lastIndexOf' p i s = let i' = String.length (take i s) in (\k -> length (String.take k s)) <$> String.lastIndexOf' p i' s +-- | Returns the number of code points in the given string. Operates in +-- | constant space and time linear to the length of the string. length :: String -> Int length = Array.length <<< toCodePointArray +-- | Creates a string containing just the given code point. Operates in +-- | constant space and time. singleton :: CodePoint -> String singleton = _singleton singletonFallback @@ -173,6 +202,9 @@ singletonFallback (CodePoint cp) = fromCharCode lead <> fromCharCode trail trail = (cp - 0x10000) `mod` 0x400 + 0xDC00 +-- | 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 } splitAt i s = let cps = toCodePointArray s in @@ -184,6 +216,10 @@ splitAt i 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 +-- | points, returns the empty string. Operates in space and time linear to the +-- | given number. take :: Int -> String -> String take = _take takeFallback @@ -193,10 +229,15 @@ takeFallback :: Int -> String -> String takeFallback n s = fromCodePointArray (Array.take n (toCodePointArray s)) +-- | Returns a string containing the leading sequence of code points which all +-- | match the given predicate from the given string. Operates in space and +-- | time linear to the given number. takeWhile :: (CodePoint -> Boolean) -> String -> String takeWhile p s = take (count p s) s +-- | Creates an array of code points from a string. Operates in space and time +-- | linear to the length of the given string. toCodePointArray :: String -> Array CodePoint toCodePointArray = _toCodePointArray toCodePointArrayFallback unsafeCodePointAt0 @@ -216,5 +257,8 @@ toCodePointArrayFallback s = unfoldr decode (fromFoldable (Char.toCharCode <$> S decode Nil = Nothing +-- | Returns a record with the first code point and the remaining code points +-- | of the given string. Returns Nothing if the string is empty. Operates in +-- | space and time linear to the length of the string. uncons :: String -> Maybe { head :: CodePoint, tail :: String } uncons s = { head: _, tail: drop 1 s } <$> codePointAt 0 s From cef521afe2f8379f20d60ad62608fe5e8c9b6c14 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Thu, 6 Jul 2017 10:10:44 -0700 Subject: [PATCH 098/186] update Data.String import warning comment --- src/Data/String/CodePoints.purs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 31b7747..93b27bc 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -33,7 +33,8 @@ import Data.List (List(Cons, Nil), fromFoldable) import Data.Maybe (Maybe(Just, Nothing)) import Data.String as String import Data.String.Unsafe as Unsafe --- WARN: This list must be updated to re-export any exports added to Data.String. That makes me sad. +-- WARN: In order for this module to be a drop-in replacement for Data.String, +-- this list must be updated to re-export any exports added to Data.String. import Data.String (Pattern(..), Replacement(..), charAt, charCodeAt, contains, fromCharArray, joinWith, localeCompare, null, replace, replaceAll, split, stripPrefix, stripSuffix, toChar, toCharArray, toLower, toUpper, trim) as StringReExports import Data.Tuple (Tuple(Tuple)) import Data.Unfoldable (unfoldr) From b38eb80e1ea9f212f889af56f6b99be00614e583 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Fri, 7 Jul 2017 02:22:08 -0700 Subject: [PATCH 099/186] refactor to avoid lists dep; better complexity adherence in fallbacks --- bower.json | 1 - src/Data/String/CodePoints.purs | 31 ++++++++++++++++++++----------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/bower.json b/bower.json index a1fa92d..5178621 100644 --- a/bower.json +++ b/bower.json @@ -22,7 +22,6 @@ "purescript-maybe": "^3.0.0", "purescript-partial": "^1.2.0", "purescript-unfoldable": "^3.0.0", - "purescript-lists": "^4.1.1", "purescript-arrays": "^4.0.1" }, "devDependencies": { diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 93b27bc..eb02577 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -29,7 +29,6 @@ import Prelude import Data.Array as Array import Data.Char as Char -import Data.List (List(Cons, Nil), fromFoldable) import Data.Maybe (Maybe(Just, Nothing)) import Data.String as String import Data.String.Unsafe as Unsafe @@ -106,7 +105,9 @@ foreign import _codePointAt -> Maybe CodePoint codePointAtFallback :: Int -> String -> Maybe CodePoint -codePointAtFallback n s = Array.index (toCodePointArray s) n +codePointAtFallback n s = case uncons s of + Just { head, tail } -> if n == 0 then Just head else codePointAtFallback (n - 1) tail + _ -> Nothing -- | Returns the number of code points in the leading sequence of code points @@ -129,7 +130,7 @@ foreign import _count -- | empty string. Operates in space and time linear to the length of the given -- | string. drop :: Int -> String -> String -drop n s = fromCodePointArray (Array.drop n (toCodePointArray s)) +drop n s = String.drop (String.length (take n s)) s -- | Drops the leading sequence of code points which all match the given @@ -227,7 +228,10 @@ take = _take takeFallback foreign import _take :: (Int -> String -> String) -> Int -> String -> String takeFallback :: Int -> String -> String -takeFallback n s = fromCodePointArray (Array.take n (toCodePointArray s)) +takeFallback n _ | n < 1 = "" +takeFallback n s = case uncons s of + Just { head, tail } -> singleton head <> takeFallback (n - 1) tail + _ -> s -- | Returns a string containing the leading sequence of code points which all @@ -249,17 +253,22 @@ foreign import _toCodePointArray -> Array CodePoint toCodePointArrayFallback :: String -> Array CodePoint -toCodePointArrayFallback s = unfoldr decode (fromFoldable (Char.toCharCode <$> String.toCharArray s)) +toCodePointArrayFallback s = unfoldr decode s where - decode :: List Int -> Maybe (Tuple CodePoint (List Int)) - decode (Cons cu0 (Cons cu1 rest)) | isLead cu0 && isTrail cu1 - = Just (Tuple (unsurrogate cu0 cu1) rest) - decode (Cons cu rest) = Just (Tuple (CodePoint cu) rest) - decode Nil = Nothing + decode :: String -> Maybe (Tuple CodePoint String) + decode s' = (\{ head, tail } -> Tuple head tail) <$> uncons s' -- | Returns a record with the first code point and the remaining code points -- | of the given string. Returns Nothing if the string is empty. Operates in -- | space and time linear to the length of the string. uncons :: String -> Maybe { head :: CodePoint, tail :: String } -uncons s = { head: _, tail: drop 1 s } <$> codePointAt 0 s +uncons s = case String.length s of + 0 -> Nothing + 1 -> Just { head: CodePoint (Unsafe.charCodeAt 0 s), tail: "" } + _ -> + let cu0 = Unsafe.charCodeAt 0 s in + let cu1 = Unsafe.charCodeAt 1 s in + if isLead cu0 && isTrail cu1 + then Just { head: unsurrogate cu0 cu1, tail: String.drop 2 s } + else Just { head: CodePoint cu0, tail: String.drop 1 s } From 4f3d71d72b5415d6ab043efd0e918e0ce17291ab Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Fri, 7 Jul 2017 02:24:30 -0700 Subject: [PATCH 100/186] remove fallback to Array.from in codePointAt JS implementation for now --- src/Data/String/CodePoints.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Data/String/CodePoints.js b/src/Data/String/CodePoints.js index 041e275..e968c32 100644 --- a/src/Data/String/CodePoints.js +++ b/src/Data/String/CodePoints.js @@ -31,10 +31,6 @@ exports._codePointAt = function (fallback) { if (o.done) return Nothing; if (i === 0) return Just(unsafeCodePointAt0(o.value)); } - } else if (hasArrayFrom) { - var cps = Array.from(str); - if (index >= cps.length) return Nothing; - return Just(unsafeCodePointAt0(cps[index])); } return fallback(index)(str); }; From e3cea19e06a7e51fbb6313d217a7d0a3f5c9d7b3 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Fri, 7 Jul 2017 02:28:10 -0700 Subject: [PATCH 101/186] prefer let over where --- src/Data/String/CodePoints.purs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index eb02577..22e6930 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -79,12 +79,11 @@ foreign import _unsafeCodePointAt0 unsafeCodePointAt0Fallback :: String -> CodePoint unsafeCodePointAt0Fallback s = + let cu0 = Unsafe.charCodeAt 0 s in + let cu1 = Unsafe.charCodeAt 1 s in if isLead cu0 && isTrail cu1 then unsurrogate cu0 cu1 else CodePoint cu0 - where - cu0 = Unsafe.charCodeAt 0 s - cu1 = Unsafe.charCodeAt 1 s -- | Returns the first code point of the string after dropping the given number @@ -198,10 +197,10 @@ foreign import _singleton singletonFallback :: CodePoint -> String singletonFallback (CodePoint cp) | cp <= 0xFFFF = fromCharCode cp -singletonFallback (CodePoint cp) = fromCharCode lead <> fromCharCode trail - where - lead = ((cp - 0x10000) / 0x400) + 0xD800 - trail = (cp - 0x10000) `mod` 0x400 + 0xDC00 +singletonFallback (CodePoint cp) = + let lead = ((cp - 0x10000) / 0x400) + 0xD800 in + let trail = (cp - 0x10000) `mod` 0x400 + 0xDC00 in + fromCharCode lead <> fromCharCode trail -- | Returns a record with strings created from the code points on either side From db3eba31fd1e15994a28a359b31249b30956200f Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Fri, 7 Jul 2017 09:20:46 -0700 Subject: [PATCH 102/186] change JS implementation of count to use string iterator if possible --- src/Data/String/CodePoints.js | 27 ++++++++++----------------- src/Data/String/CodePoints.purs | 16 ++++++++++++---- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/Data/String/CodePoints.js b/src/Data/String/CodePoints.js index e968c32..1c73483 100644 --- a/src/Data/String/CodePoints.js +++ b/src/Data/String/CodePoints.js @@ -40,29 +40,22 @@ exports._codePointAt = function (fallback) { }; }; -exports._count = function (isLead) { - return function (isTrail) { - return function (unsurrogate) { +exports._count = function (fallback) { + return function (unsafeCodePointAt0) { + if (hasStringIterator) { return function (pred) { return function (str) { - var cpCount = 0; - for (var cuCount = 0; cuCount < str.length; ++cuCount) { - var lead = str.charCodeAt(cuCount); - var cp = lead; - if (isLead(lead) && cuCount + 1 < str.length) { - var trail = str.charCodeAt(cuCount + 1); - if (isTrail(trail)) { - cp = unsurrogate(lead)(trail); - ++cuCount; - } - } + var iter = str[Symbol.iterator](); + for (var cpCount = 0; ; ++cpCount) { + var o = iter.next(); + if (o.done) return cpCount; + var cp = unsafeCodePointAt0(o.value); if (!pred(cp)) return cpCount; - ++cpCount; } - return cpCount; }; }; - }; + } + return fallback; }; }; diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 22e6930..1638d93 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -90,6 +90,7 @@ unsafeCodePointAt0Fallback s = -- | of code points from the beginning, if there is such a code point. Operates -- | in constant space and in time linear to `n`. codePointAt :: Int -> String -> Maybe CodePoint +codePointAt n _ | n < 0 = Nothing codePointAt 0 "" = Nothing codePointAt 0 s = Just (unsafeCodePointAt0 s) codePointAt n s = _codePointAt codePointAtFallback Just Nothing unsafeCodePointAt0 n s @@ -113,16 +114,23 @@ codePointAtFallback n s = case uncons s of -- | which all match the given predicate. Operates in constant space and in -- | time linear to the length of the given string. count :: (CodePoint -> Boolean) -> String -> Int -count = _count isLead isTrail unsurrogate +count = _count countFallback unsafeCodePointAt0 foreign import _count - :: (Int -> Boolean) - -> (Int -> Boolean) - -> (Int -> Int -> CodePoint) + :: ((CodePoint -> Boolean) -> String -> Int) + -> (String -> CodePoint) -> (CodePoint -> Boolean) -> String -> Int +countFallback :: (CodePoint -> Boolean) -> String -> Int +countFallback p s = countTail p s 0 + where + countTail :: (CodePoint -> Boolean) -> String -> Int -> Int + countTail p' s' accum = case uncons s' of + Just { head, tail } -> if p' head then countTail p' tail (accum + 1) else accum + _ -> accum + -- | Drops the given number of code points from the beginning of the given -- | string. If the string does not have that many code points, returns the From 3a24c8d00b40a61fed564c9a6d0a154b180d8196 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Fri, 7 Jul 2017 09:39:05 -0700 Subject: [PATCH 103/186] update comments --- src/Data/String/CodePoints.purs | 51 +++++++++++++++++---------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 1638d93..e71560a 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -32,8 +32,12 @@ import Data.Char as Char import Data.Maybe (Maybe(Just, Nothing)) import Data.String as String import Data.String.Unsafe as Unsafe --- WARN: In order for this module to be a drop-in replacement for Data.String, --- this list must be updated to re-export any exports added to Data.String. +-- WARN: If a new function is added to Data.String, a version of that function +-- should be exported from this module, which should be the same except that it +-- should operate on the code point level rather than the code unit level. If +-- the function's behaviour does not change based on whether we consider +-- strings as sequences of code points or code units, it can simply be +-- re-exported from Data.String. import Data.String (Pattern(..), Replacement(..), charAt, charCodeAt, contains, fromCharArray, joinWith, localeCompare, null, replace, replaceAll, split, stripPrefix, stripSuffix, toChar, toCharArray, toLower, toUpper, trim) as StringReExports import Data.Tuple (Tuple(Tuple)) import Data.Unfoldable (unfoldr) @@ -88,7 +92,7 @@ unsafeCodePointAt0Fallback s = -- | Returns the first code point of the string after dropping the given number -- | of code points from the beginning, if there is such a code point. Operates --- | in constant space and in time linear to `n`. +-- | in constant space and in time linear to the given index. codePointAt :: Int -> String -> Maybe CodePoint codePointAt n _ | n < 0 = Nothing codePointAt 0 "" = Nothing @@ -112,7 +116,7 @@ codePointAtFallback n s = case uncons s of -- | Returns the number of code points in the leading sequence of code points -- | which all match the given predicate. Operates in constant space and in --- | time linear to the length of the given string. +-- | time linear to the length of the string. count :: (CodePoint -> Boolean) -> String -> Int count = _count countFallback unsafeCodePointAt0 @@ -132,23 +136,22 @@ countFallback p s = countTail p s 0 _ -> accum --- | Drops the given number of code points from the beginning of the given --- | string. If the string does not have that many code points, returns the --- | empty string. Operates in space and time linear to the length of the given --- | string. +-- | Drops the given number of code points from the beginning of the string. If +-- | the string does not have that many code points, returns the empty string. +-- | Operates in space and time linear to the length of the string. drop :: Int -> String -> String drop n s = String.drop (String.length (take n s)) s -- | Drops the leading sequence of code points which all match the given --- | predicate from the given string. Operates in space and time linear to the --- | length of the given string. +-- | predicate from the string. Operates in space and time linear to the +-- | length of the string. dropWhile :: (CodePoint -> Boolean) -> String -> String dropWhile p s = drop (count p s) s -- | Creates a string from an array of code points. Operates in space and time --- | linear to the length of the given array. +-- | linear to the length of the array. fromCodePointArray :: Array CodePoint -> String fromCodePointArray = _fromCodePointArray singletonFallback @@ -158,14 +161,14 @@ foreign import _fromCodePointArray -> String -- | Returns the number of code points preceding the first match of the given --- | pattern in the given string. Returns Nothing when no matches are found. +-- | pattern in the string. Returns Nothing when no matches are found. indexOf :: String.Pattern -> String -> Maybe Int indexOf p s = (\i -> length (String.take i s)) <$> String.indexOf p s -- | Returns the number of code points preceding the first match of the given --- | pattern in the given string. Pattern matches preceding the given index --- | will be ignored. Returns Nothing when no matches are found. +-- | pattern in the string. Pattern matches preceding the given index will be +-- | ignored. Returns Nothing when no matches are found. indexOf' :: String.Pattern -> Int -> String -> Maybe Int indexOf' p i s = let s' = drop i s in @@ -173,22 +176,22 @@ indexOf' p i s = -- | Returns the number of code points preceding the last match of the given --- | pattern in the given string. Returns Nothing when no matches are found. +-- | pattern in the string. Returns Nothing when no matches are found. lastIndexOf :: String.Pattern -> String -> Maybe Int lastIndexOf p s = (\i -> length (String.take i s)) <$> String.lastIndexOf p s -- | Returns the number of code points preceding the first match of the given --- | pattern in the given string. Pattern matches following the given index --- | will be ignored. Returns Nothing when no matches are found. +-- | pattern in the string. Pattern matches following the given index will be +-- | ignored. Returns Nothing when no matches are found. lastIndexOf' :: String.Pattern -> Int -> String -> Maybe Int lastIndexOf' p i s = let i' = String.length (take i s) in (\k -> length (String.take k s)) <$> String.lastIndexOf' p i' s --- | Returns the number of code points in the given string. Operates in --- | constant space and time linear to the length of the string. +-- | Returns the number of code points in the string. Operates in constant +-- | space and in time linear to the length of the string. length :: String -> Int length = Array.length <<< toCodePointArray @@ -242,14 +245,14 @@ takeFallback n s = case uncons s of -- | Returns a string containing the leading sequence of code points which all --- | match the given predicate from the given string. Operates in space and --- | time linear to the given number. +-- | match the given predicate from the string. Operates in space and time +-- | linear to the length of the string. takeWhile :: (CodePoint -> Boolean) -> String -> String takeWhile p s = take (count p s) s -- | Creates an array of code points from a string. Operates in space and time --- | linear to the length of the given string. +-- | linear to the length of the string. toCodePointArray :: String -> Array CodePoint toCodePointArray = _toCodePointArray toCodePointArrayFallback unsafeCodePointAt0 @@ -267,8 +270,8 @@ toCodePointArrayFallback s = unfoldr decode s -- | Returns a record with the first code point and the remaining code points --- | of the given string. Returns Nothing if the string is empty. Operates in --- | space and time linear to the length of the string. +-- | of the string. Returns Nothing if the string is empty. Operates in +-- | constant space and time. uncons :: String -> Maybe { head :: CodePoint, tail :: String } uncons s = case String.length s of 0 -> Nothing From 82a502f39ba8a8d52c29291da6350dffc3736f0b Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Fri, 7 Jul 2017 22:27:36 -0700 Subject: [PATCH 104/186] pull functions out of where clauses --- src/Data/String/CodePoints.purs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index e71560a..c0290a1 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -129,11 +129,11 @@ foreign import _count countFallback :: (CodePoint -> Boolean) -> String -> Int countFallback p s = countTail p s 0 - where - countTail :: (CodePoint -> Boolean) -> String -> Int -> Int - countTail p' s' accum = case uncons s' of - Just { head, tail } -> if p' head then countTail p' tail (accum + 1) else accum - _ -> accum + +countTail :: (CodePoint -> Boolean) -> String -> Int -> Int +countTail p s accum = case uncons s of + Just { head, tail } -> if p head then countTail p tail (accum + 1) else accum + _ -> accum -- | Drops the given number of code points from the beginning of the string. If @@ -263,10 +263,10 @@ foreign import _toCodePointArray -> Array CodePoint toCodePointArrayFallback :: String -> Array CodePoint -toCodePointArrayFallback s = unfoldr decode s - where - decode :: String -> Maybe (Tuple CodePoint String) - decode s' = (\{ head, tail } -> Tuple head tail) <$> uncons s' +toCodePointArrayFallback s = unfoldr unconsButWithTuple s + +unconsButWithTuple :: String -> Maybe (Tuple CodePoint String) +unconsButWithTuple s' = (\{ head, tail } -> Tuple head tail) <$> uncons s' -- | Returns a record with the first code point and the remaining code points From 085022e290d1c42f94714723fa39b410eece3157 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Fri, 7 Jul 2017 22:31:16 -0700 Subject: [PATCH 105/186] change complexity documentation for drop{,While} and take{,While} --- src/Data/String/CodePoints.purs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index c0290a1..b4bca52 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -138,14 +138,14 @@ countTail p s accum = case uncons s of -- | Drops the given number of code points from the beginning of the string. If -- | the string does not have that many code points, returns the empty string. --- | Operates in space and time linear to the length of the string. +-- | Operates in constant space and in time linear to the given number. drop :: Int -> String -> String drop n s = String.drop (String.length (take n s)) s -- | Drops the leading sequence of code points which all match the given --- | predicate from the string. Operates in space and time linear to the --- | length of the string. +-- | predicate from the string. Operates in constant space and in time linear +-- | to the length of the string. dropWhile :: (CodePoint -> Boolean) -> String -> String dropWhile p s = drop (count p s) s @@ -230,8 +230,8 @@ splitAt i 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 --- | points, returns the empty string. Operates in space and time linear to the --- | given number. +-- | points, returns the empty string. Operates in constant space and in time +-- | linear to the given number. take :: Int -> String -> String take = _take takeFallback @@ -245,8 +245,8 @@ takeFallback n s = case uncons s of -- | Returns a string containing the leading sequence of code points which all --- | match the given predicate from the string. Operates in space and time --- | linear to the length of the string. +-- | match the given predicate from the string. Operates in constant space and +-- | in time linear to the length of the string. takeWhile :: (CodePoint -> Boolean) -> String -> String takeWhile p s = take (count p s) s From 6edb70fd2f7fea69502025cb90882008004e79b1 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Sat, 8 Jul 2017 07:15:55 -0700 Subject: [PATCH 106/186] forgot about a prime --- src/Data/String/CodePoints.purs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index b4bca52..7fe51f3 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -266,7 +266,7 @@ toCodePointArrayFallback :: String -> Array CodePoint toCodePointArrayFallback s = unfoldr unconsButWithTuple s unconsButWithTuple :: String -> Maybe (Tuple CodePoint String) -unconsButWithTuple s' = (\{ head, tail } -> Tuple head tail) <$> uncons s' +unconsButWithTuple s = (\{ head, tail } -> Tuple head tail) <$> uncons s -- | Returns a record with the first code point and the remaining code points From 80238f506581ee1375ffd330c56d8156ebb85c1d Mon Sep 17 00:00:00 2001 From: Harry Garrood Date: Mon, 10 Jul 2017 16:19:41 +0100 Subject: [PATCH 107/186] v3.3.0 From 43d8699de8106ebb8e0124a32bc4c9e0732191d9 Mon Sep 17 00:00:00 2001 From: Phil Ruffwind Date: Sun, 6 Aug 2017 14:45:56 -0400 Subject: [PATCH 108/186] Quote strings in Show instances (#86) --- src/Data/String.purs | 4 ++-- src/Data/String/CaseInsensitiveString.purs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Data/String.purs b/src/Data/String.purs index 22be682..6092ef6 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -50,7 +50,7 @@ derive instance ordPattern :: Ord Pattern derive instance newtypePattern :: Newtype Pattern _ instance showPattern :: Show Pattern where - show (Pattern s) = "(Pattern " <> s <> ")" + show (Pattern s) = "(Pattern " <> show s <> ")" -- | A newtype used in cases to specify a replacement for a pattern. newtype Replacement = Replacement String @@ -60,7 +60,7 @@ derive instance ordReplacement :: Ord Replacement derive instance newtypeReplacement :: Newtype Replacement _ instance showReplacement :: Show Replacement where - show (Replacement s) = "(Replacement " <> s <> ")" + show (Replacement s) = "(Replacement " <> show s <> ")" -- | Returns the character at the given index, if the index is within bounds. charAt :: Int -> String -> Maybe Char diff --git a/src/Data/String/CaseInsensitiveString.purs b/src/Data/String/CaseInsensitiveString.purs index 124810f..3783164 100644 --- a/src/Data/String/CaseInsensitiveString.purs +++ b/src/Data/String/CaseInsensitiveString.purs @@ -17,6 +17,6 @@ instance ordCaseInsensitiveString :: Ord CaseInsensitiveString where compare (toLower s1) (toLower s2) instance showCaseInsensitiveString :: Show CaseInsensitiveString where - show (CaseInsensitiveString s) = "(CaseInsensitiveString " <> s <> ")" + show (CaseInsensitiveString s) = "(CaseInsensitiveString " <> show s <> ")" derive instance newtypeCaseInsensitiveString :: Newtype CaseInsensitiveString _ From ad0bf437dd8a47db12305e6329eb222e00faf96e Mon Sep 17 00:00:00 2001 From: Nicholas Scheel Date: Fri, 11 Aug 2017 16:30:52 -0500 Subject: [PATCH 109/186] 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 110/186] 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 111/186] 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 112/186] 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 113/186] 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 6558a55162e598719575eb4b23c1675dee0ec272 Mon Sep 17 00:00:00 2001 From: David Peter Date: Sun, 8 Oct 2017 23:15:14 +0200 Subject: [PATCH 114/186] Add usage examples to the documentation (#88) * Add usage examples to the documentation * Detailed explanation for 'localeCompare', add results --- src/Data/String.purs | 44 +++++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/src/Data/String.purs b/src/Data/String.purs index 6092ef6..7894253 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -78,6 +78,7 @@ foreign import singleton :: Char -> String -- | Returns the numeric Unicode value of the character at the given index, -- | if the index is within bounds. +-- | - `charCodeAt 2 "5 €" == Just 0x20AC` charCodeAt :: Int -> String -> Maybe Int charCodeAt = _charCodeAt Just Nothing @@ -88,6 +89,8 @@ foreign import _charCodeAt -> String -> Maybe Int +-- | Converts the string to a character, if the length of the string is +-- | exactly `1`. toChar :: String -> Maybe Char toChar = _toChar Just Nothing @@ -109,6 +112,7 @@ uncons s = Just { head: U.charAt zero s, tail: drop one s } -- | Returns the longest prefix (possibly empty) of characters that satisfy -- | the predicate. +-- | * `takeWhile (_ /= ':') "http://purescript.org" == "http"` takeWhile :: (Char -> Boolean) -> String -> String takeWhile p s = take (count p s) s @@ -127,7 +131,8 @@ stripPrefix prefix@(Pattern prefixS) str = _ -> Nothing -- | If the string ends with the given suffix, return the portion of the --- | string left after removing it, as a Just value. Otherwise, return Nothing. +-- | string left after removing it, as a `Just` value. Otherwise, return +-- | `Nothing`. -- | * `stripSuffix (Pattern ".exe") "psc.exe" == Just "psc"` -- | * `stripSuffix (Pattern ".exe") "psc" == Nothing` stripSuffix :: Pattern -> String -> Maybe String @@ -139,12 +144,15 @@ stripSuffix suffix@(Pattern suffixS) str = -- | Converts an array of characters into a string. foreign import fromCharArray :: Array Char -> String --- | Checks whether the first string exists in the second string. +-- | Checks whether the pattern appears in the given string. +-- | * `contains (Pattern "needle") "haystack with needle" == true` +-- | * `contains (Pattern "needle") "haystack" == false` contains :: Pattern -> String -> Boolean contains pat = isJust <<< indexOf pat --- | Returns the index of the first occurrence of the first string in the --- | second string. Returns `Nothing` if there is no match. +-- | Returns the index of the first occurrence of the pattern in the +-- | given string. Returns `Nothing` if there is no match. +-- | * `indexOf (Pattern "c") "abcde" == Just 2` indexOf :: Pattern -> String -> Maybe Int indexOf = _indexOf Just Nothing @@ -155,8 +163,8 @@ foreign import _indexOf -> String -> Maybe Int --- | Returns the index of the first occurrence of the first string in the --- | second string, starting at the given index. Returns `Nothing` if there is +-- | Returns the index of the first occurrence of the pattern in the +-- | given string, starting at the specified index. Returns `Nothing` if there is -- | no match. indexOf' :: Pattern -> Int -> String -> Maybe Int indexOf' = _indexOf' Just Nothing @@ -169,8 +177,8 @@ foreign import _indexOf' -> String -> Maybe Int --- | Returns the index of the last occurrence of the first string in the --- | second string. Returns `Nothing` if there is no match. +-- | Returns the index of the last occurrence of the pattern in the +-- | given string. Returns `Nothing` if there is no match. lastIndexOf :: Pattern -> String -> Maybe Int lastIndexOf = _lastIndexOf Just Nothing @@ -181,9 +189,9 @@ foreign import _lastIndexOf -> String -> Maybe Int --- | Returns the index of the last occurrence of the first string in the --- | second string, starting at the given index. Returns `Nothing` if there is --- | no match. +-- | Returns the index of the last occurrence of the pattern in the +-- | given string, starting at the specified index. Returns `Nothing` +-- | if there is no match. lastIndexOf' :: Pattern -> Int -> String -> Maybe Int lastIndexOf' = _lastIndexOf' Just Nothing @@ -198,7 +206,11 @@ foreign import _lastIndexOf' -- | Returns the number of characters the string is composed of. foreign import length :: String -> Int --- | Locale-aware sort order comparison. +-- | Compare two strings in a locale-aware fashion. This is in contrast to +-- | the `Ord` instance on `String` which treats strings as arrays of code +-- | units: +-- | - `"Γ€" `localeCompare` "b" == LT` +-- | - `"Γ€" `compare` "b" == GT` localeCompare :: String -> String -> Ordering localeCompare = _localeCompare LT EQ GT @@ -210,10 +222,11 @@ foreign import _localeCompare -> String -> Ordering --- | Replaces the first occurence of the first argument with the second argument. +-- | Replaces the first occurence of the pattern with the replacement string. +-- | * `replace (Pattern "http") (Replacement "https") "http://purescript.org" == "https://purescript.org"` foreign import replace :: Pattern -> Replacement -> String -> String --- | Replaces all occurences of the first argument with the second argument. +-- | Replaces all occurences of the pattern with the replacement string. foreign import replaceAll :: Pattern -> Replacement -> String -> String -- | Returns the first `n` characters of the string. @@ -231,7 +244,7 @@ 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. +-- | Returns the substrings of a split at the given index, if the index is within bounds. splitAt :: Int -> String -> Maybe { before :: String, after :: String } splitAt = _splitAt Just Nothing @@ -257,4 +270,5 @@ foreign import trim :: String -> String -- | Joins the strings in the array together, inserting the first argument -- | as separator between them. +-- | * `joinWith ", " ["apple", "banana", "orange"] == "apple, banana, orange"` foreign import joinWith :: String -> Array String -> String From 022f93280646eb0fc4c9670ff2b08a8232ceec31 Mon Sep 17 00:00:00 2001 From: Stefan Fehrenbach Date: Sun, 19 Nov 2017 19:22:47 +0000 Subject: [PATCH 115/186] Reuse the array returned by match (#89) Instead of basically making a copy and letting the original go out of scope immediately after. This makes a surprisingly large performance difference in Chrome, not so much in Firefox. --- src/Data/String/Regex.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Data/String/Regex.js b/src/Data/String/Regex.js index 1d7e407..2e3ac3b 100644 --- a/src/Data/String/Regex.js +++ b/src/Data/String/Regex.js @@ -49,11 +49,10 @@ exports._match = function (just) { if (m == null) { return nothing; } else { - var list = []; for (var i = 0; i < m.length; i++) { - list.push(m[i] == null ? nothing : just(m[i])); + m[i] = m[i] == null ? nothing : just(m[i]); } - return just(list); + return just(m); } }; }; From b3485f66f800e8023b8e7d55fa1fc681a4b2823e Mon Sep 17 00:00:00 2001 From: Carsten Csiky Date: Sat, 16 Dec 2017 20:08:39 +0100 Subject: [PATCH 116/186] created examples for `String` functions and refactored existing ones (#90) --- src/Data/String.purs | 191 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 175 insertions(+), 16 deletions(-) diff --git a/src/Data/String.purs b/src/Data/String.purs index 7894253..f927a3b 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -43,6 +43,14 @@ import Data.Newtype (class Newtype) import Data.String.Unsafe as U -- | A newtype used in cases where there is a string to be matched. +-- | +-- | ```purescript +-- | pursPattern = Pattern ".purs" +-- | --can be used like this: +-- | contains pursPattern "Test.purs" +-- | == true +-- | ``` +-- | newtype Pattern = Pattern String derive instance eqPattern :: Eq Pattern @@ -63,6 +71,12 @@ instance showReplacement :: Show Replacement where show (Replacement s) = "(Replacement " <> show s <> ")" -- | Returns the character at the given index, if the index is within bounds. +-- | +-- | ```purescript +-- | charAt 2 "Hello" == Just 'l' +-- | charAt 10 "Hello" == Nothing +-- | ``` +-- | charAt :: Int -> String -> Maybe Char charAt = _charAt Just Nothing @@ -74,11 +88,20 @@ foreign import _charAt -> Maybe Char -- | Returns a string of length `1` containing the given character. +-- | +-- | ```purescript +-- | singleton 'l' == "l" +-- | ``` +-- | foreign import singleton :: Char -> String -- | Returns the numeric Unicode value of the character at the given index, -- | if the index is within bounds. --- | - `charCodeAt 2 "5 €" == Just 0x20AC` +-- | ```purescript +-- | charCodeAt 2 "5 €" == Just 0x20AC +-- | charCodeAt 10 "5 €" == Nothing +-- | ``` +-- | charCodeAt :: Int -> String -> Maybe Int charCodeAt = _charCodeAt Just Nothing @@ -91,6 +114,12 @@ foreign import _charCodeAt -- | Converts the string to a character, if the length of the string is -- | exactly `1`. +-- | +-- | ```purescript +-- | toChar "l" == Just 'l' +-- | toChar "Hi" == Nothing -- since length is not 1 +-- | ``` +-- | toChar :: String -> Maybe Char toChar = _toChar Just Nothing @@ -101,29 +130,54 @@ foreign import _toChar -> Maybe Char -- | Returns `true` if the given string is empty. +-- | +-- | ```purescript +-- | null "" == true +-- | null "Hi" == false +-- | ``` +-- | null :: String -> Boolean null s = s == "" -- | Returns the first character and the rest of the string, -- | if the string is not empty. +-- | +-- | ```purescript +-- | uncons "" == Nothing +-- | uncons "Hello World" == Just { head: 'H', tail: "ello World" } +-- | ``` +-- | uncons :: String -> Maybe { head :: Char, tail :: String } uncons "" = Nothing uncons s = Just { head: U.charAt zero s, tail: drop one s } -- | Returns the longest prefix (possibly empty) of characters that satisfy -- | the predicate. --- | * `takeWhile (_ /= ':') "http://purescript.org" == "http"` +-- | +-- | ```purescript +-- | takeWhile (_ /= ':') "http://purescript.org" == "http" +-- | ``` +-- | takeWhile :: (Char -> Boolean) -> String -> String takeWhile p s = take (count p s) s -- | Returns the suffix remaining after `takeWhile`. +-- | +-- | ```purescript +-- | dropWhile (_ /= '.') "Test.purs" == ".purs" +-- | ``` +-- | dropWhile :: (Char -> Boolean) -> String -> String dropWhile p s = drop (count p s) s -- | If the string starts with the given prefix, return the portion of the -- | string left after removing it, as a Just value. Otherwise, return Nothing. --- | * `stripPrefix (Pattern "http:") "http://purescript.org" == Just "//purescript.org"` --- | * `stripPrefix (Pattern "http:") "https://purescript.org" == Nothing` +-- | +-- | ```purescript +-- | stripPrefix (Pattern "http:") "http://purescript.org" == Just "//purescript.org" +-- | stripPrefix (Pattern "http:") "https://purescript.org" == Nothing +-- | ``` +-- | stripPrefix :: Pattern -> String -> Maybe String stripPrefix prefix@(Pattern prefixS) str = case indexOf prefix str of @@ -133,8 +187,12 @@ stripPrefix prefix@(Pattern prefixS) str = -- | If the string ends with the given suffix, return the portion of the -- | string left after removing it, as a `Just` value. Otherwise, return -- | `Nothing`. --- | * `stripSuffix (Pattern ".exe") "psc.exe" == Just "psc"` --- | * `stripSuffix (Pattern ".exe") "psc" == Nothing` +-- | +-- | ```purescript +-- | stripSuffix (Pattern ".exe") "psc.exe" == Just "psc" +-- | stripSuffix (Pattern ".exe") "psc" == Nothing +-- | ``` +-- | stripSuffix :: Pattern -> String -> Maybe String stripSuffix suffix@(Pattern suffixS) str = case lastIndexOf suffix str of @@ -142,17 +200,31 @@ stripSuffix suffix@(Pattern suffixS) str = _ -> Nothing -- | Converts an array of characters into a string. +-- | +-- | ```purescript +-- | fromCharArray ['H', 'e', 'l', 'l', 'o'] == "Hello" +-- | ``` +-- | foreign import fromCharArray :: Array Char -> String -- | Checks whether the pattern appears in the given string. --- | * `contains (Pattern "needle") "haystack with needle" == true` --- | * `contains (Pattern "needle") "haystack" == false` +-- | +-- | ```purescript +-- | contains (Pattern "needle") "haystack with needle" == true +-- | contains (Pattern "needle") "haystack" == false +-- | ``` +-- | contains :: Pattern -> String -> Boolean contains pat = isJust <<< indexOf pat -- | Returns the index of the first occurrence of the pattern in the -- | given string. Returns `Nothing` if there is no match. --- | * `indexOf (Pattern "c") "abcde" == Just 2` +-- | +-- | ```purescript +-- | indexOf (Pattern "c") "abcdc" == Just 2 +-- | indexOf (Pattern "c") "aaa" == Nothing +-- | ``` +-- | indexOf :: Pattern -> String -> Maybe Int indexOf = _indexOf Just Nothing @@ -166,6 +238,12 @@ foreign import _indexOf -- | Returns the index of the first occurrence of the pattern in the -- | given string, starting at the specified index. Returns `Nothing` if there is -- | no match. +-- | +-- | ```purescript +-- | indexOf' (Pattern "a") 2 "ababa" == Just 2 +-- | indexOf' (Pattern "a") 3 "ababa" == Just 4 +-- | ``` +-- | indexOf' :: Pattern -> Int -> String -> Maybe Int indexOf' = _indexOf' Just Nothing @@ -179,6 +257,12 @@ foreign import _indexOf' -- | Returns the index of the last occurrence of the pattern in the -- | given string. Returns `Nothing` if there is no match. +-- | +-- | ```purescript +-- | lastIndexOf (Pattern "c") "abcdc" == Just 4 +-- | lastIndexOf (Pattern "c") "aaa" == Nothing +-- | ``` +-- | lastIndexOf :: Pattern -> String -> Maybe Int lastIndexOf = _lastIndexOf Just Nothing @@ -190,8 +274,16 @@ foreign import _lastIndexOf -> Maybe Int -- | Returns the index of the last occurrence of the pattern in the --- | given string, starting at the specified index. Returns `Nothing` --- | if there is no match. +-- | given string, starting at the specified index +-- | and searching backwards towards the beginning of the string. +-- | Returns `Nothing` if there is no match. +-- | +-- | ```purescript +-- | lastIndexOf' (Pattern "a") 1 "ababa" == Just 0 +-- | lastIndexOf' (Pattern "a") 3 "ababa" == Just 2 +-- | lastIndexOf' (Pattern "a") 4 "ababa" == Just 4 +-- | ``` +-- | lastIndexOf' :: Pattern -> Int -> String -> Maybe Int lastIndexOf' = _lastIndexOf' Just Nothing @@ -204,13 +296,22 @@ foreign import _lastIndexOf' -> Maybe Int -- | Returns the number of characters the string is composed of. +-- | +-- | ```purescript +-- | length "Hello World" == 11 +-- | ``` +-- | foreign import length :: String -> Int -- | Compare two strings in a locale-aware fashion. This is in contrast to -- | the `Ord` instance on `String` which treats strings as arrays of code -- | units: --- | - `"Γ€" `localeCompare` "b" == LT` --- | - `"Γ€" `compare` "b" == GT` +-- | +-- | ```purescript +-- | "Γ€" `localeCompare` "b" == LT +-- | "Γ€" `compare` "b" == GT +-- | ``` +-- | localeCompare :: String -> String -> Ordering localeCompare = _localeCompare LT EQ GT @@ -223,28 +324,62 @@ foreign import _localeCompare -> Ordering -- | Replaces the first occurence of the pattern with the replacement string. --- | * `replace (Pattern "http") (Replacement "https") "http://purescript.org" == "https://purescript.org"` +-- | +-- | ```purescript +-- | replace (Pattern "<=") (Replacement "≀") "a <= b <= c" == "a ≀ b <= c" +-- | ``` +-- | foreign import replace :: Pattern -> Replacement -> String -> String -- | Replaces all occurences of the pattern with the replacement string. +-- | +-- | ```purescript +-- | replaceAll (Pattern "<=") (Replacement "≀") "a <= b <= c" == "a ≀ b ≀ c" +-- | ``` +-- | foreign import replaceAll :: Pattern -> Replacement -> String -> String -- | Returns the first `n` characters of the string. +-- | +-- | ```purescript +-- | take 5 "Hello World" == "Hello" +-- | ``` +-- | foreign import take :: Int -> String -> String -- | Returns the string without the first `n` characters. +-- | +-- | ```purescript +-- | drop 6 "Hello World" == "World" +-- | ``` +-- | foreign import drop :: Int -> String -> String -- | Returns the number of contiguous characters at the beginning -- | of the string for which the predicate holds. +-- | +-- | ```purescript +-- | count (_ /= ' ') "Hello World" == 5 -- since length "Hello" == 5 +-- | ``` +-- | foreign import count :: (Char -> Boolean) -> String -> Int -- | Returns the substrings of the second string separated along occurences -- | of the first string. --- | * `split (Pattern " ") "hello world" == ["hello", "world"]` +-- | +-- | ```purescript +-- | split (Pattern " ") "hello world" == ["hello", "world"] +-- | ``` +-- | foreign import split :: Pattern -> String -> Array String -- | Returns the substrings of a split at the given index, if the index is within bounds. +-- | +-- | ```purescript +-- | splitAt 2 "Hello World" == Just { before: "He", after: "llo World"} +-- | splitAt 10 "Hi" == Nothing +-- | ``` +-- | splitAt :: Int -> String -> Maybe { before :: String, after :: String } splitAt = _splitAt Just Nothing @@ -255,20 +390,44 @@ foreign import _splitAt :: (forall a. a -> Maybe a) -> Maybe { before :: String, after :: String } -- | Converts the string into an array of characters. +-- | +-- | ```purescript +-- | toCharArray "Hello☺\n" == ['H','e','l','l','o','☺','\n'] +-- | ``` +-- | foreign import toCharArray :: String -> Array Char -- | Returns the argument converted to lowercase. +-- | +-- | ```purescript +-- | toLower "hElLo" == "hello" +-- | ``` +-- | foreign import toLower :: String -> String -- | Returns the argument converted to uppercase. +-- | +-- | ```purescript +-- | toUpper "Hello" == "HELLO" +-- | ``` +-- | foreign import toUpper :: String -> String -- | Removes whitespace from the beginning and end of a string, including -- | [whitespace characters](http://www.ecma-international.org/ecma-262/5.1/#sec-7.2) -- | and [line terminators](http://www.ecma-international.org/ecma-262/5.1/#sec-7.3). +-- | +-- | ```purescript +-- | trim " Hello \n World\n\t " == "Hello \n World" +-- | ``` +-- | foreign import trim :: String -> String -- | Joins the strings in the array together, inserting the first argument -- | as separator between them. --- | * `joinWith ", " ["apple", "banana", "orange"] == "apple, banana, orange"` +-- | +-- | ```purescript +-- | joinWith ", " ["apple", "banana", "orange"] == "apple, banana, orange" +-- | ``` +-- | foreign import joinWith :: String -> Array String -> String From 66b7bbc327e5d182e7b529b87f69a72b0c79f6ea Mon Sep 17 00:00:00 2001 From: Carsten Csiky Date: Sat, 23 Dec 2017 22:14:26 +0100 Subject: [PATCH 117/186] Show Instance for CodePoint (#93) --- bower.json | 3 ++- src/Data/String/CodePoints.purs | 4 ++++ test/Test/Data/String/CodePoints.purs | 9 +++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/bower.json b/bower.json index 5178621..385c62f 100644 --- a/bower.json +++ b/bower.json @@ -22,7 +22,8 @@ "purescript-maybe": "^3.0.0", "purescript-partial": "^1.2.0", "purescript-unfoldable": "^3.0.0", - "purescript-arrays": "^4.0.1" + "purescript-arrays": "^4.0.1", + "purescript-integers": "^3.2.0" }, "devDependencies": { "purescript-assert": "^3.0.0", diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 7fe51f3..9e7a121 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -29,6 +29,7 @@ import Prelude import Data.Array as Array import Data.Char as Char +import Data.Int (hexadecimal, toStringAs) import Data.Maybe (Maybe(Just, Nothing)) import Data.String as String import Data.String.Unsafe as Unsafe @@ -50,6 +51,9 @@ newtype CodePoint = CodePoint Int derive instance eqCodePoint :: Eq CodePoint derive instance ordCodePoint :: Ord CodePoint +instance showCodePoint :: Show CodePoint where + show (CodePoint i) = "(CodePoint 0x" <> String.toUpper (toStringAs hexadecimal i) <> ")" + -- I would prefer that this smart constructor not need to exist and instead -- CodePoint just implements Enum, but the Enum module already depends on this -- one. To avoid the circular dependency, we just expose these two functions. diff --git a/test/Test/Data/String/CodePoints.purs b/test/Test/Data/String/CodePoints.purs index 7a6aef0..cd60c07 100644 --- a/test/Test/Data/String/CodePoints.purs +++ b/test/Test/Data/String/CodePoints.purs @@ -15,6 +15,15 @@ str = "a\xDC00\xD800\xD800\x16805\x16A06\&z" testStringCodePoints :: forall eff. Eff (console :: CONSOLE, assert :: ASSERT | eff) Unit testStringCodePoints = do + log "show" + assert $ map show (codePointAt 0 str) == Just "(CodePoint 0x61)" + assert $ map show (codePointAt 1 str) == Just "(CodePoint 0xDC00)" + assert $ map show (codePointAt 2 str) == Just "(CodePoint 0xD800)" + assert $ map show (codePointAt 3 str) == Just "(CodePoint 0xD800)" + assert $ map show (codePointAt 4 str) == Just "(CodePoint 0x16805)" + assert $ map show (codePointAt 5 str) == Just "(CodePoint 0x16A06)" + assert $ map show (codePointAt 6 str) == Just "(CodePoint 0x7A)" + log "codePointAt" assert $ codePointAt (-1) str == Nothing assert $ codePointAt 0 str == (codePointFromInt 0x61) From 7a2a33c2beed068733cc8ec80840362359b2848e Mon Sep 17 00:00:00 2001 From: Carsten Csiky Date: Thu, 28 Dec 2017 11:36:34 +0100 Subject: [PATCH 118/186] added function codePointFromChar (#92) --- src/Data/String/CodePoints.purs | 12 ++++++++++++ test/Test/Data/String/CodePoints.purs | 6 ++++++ 2 files changed, 18 insertions(+) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 9e7a121..1af54da 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -8,6 +8,7 @@ module Data.String.CodePoints , codePointAt , codePointFromInt , codePointToInt + , codePointFromChar , count , drop , dropWhile @@ -28,6 +29,7 @@ module Data.String.CodePoints import Prelude import Data.Array as Array +import Data.Char (toCharCode) import Data.Char as Char import Data.Int (hexadecimal, toStringAs) import Data.Maybe (Maybe(Just, Nothing)) @@ -64,6 +66,16 @@ codePointFromInt n = Nothing codePointToInt :: CodePoint -> Int codePointToInt (CodePoint n) = n +-- | Creates a CodePoint from a given Char. +-- | +-- | ```purescript +-- | >>> codePointFromChar 'B' +-- | CodePoint 0x42 -- represents 'B' +-- | ``` +-- | +codePointFromChar :: Char -> CodePoint +codePointFromChar = toCharCode >>> CodePoint + unsurrogate :: Int -> Int -> CodePoint unsurrogate lead trail = CodePoint ((lead - 0xD800) * 0x400 + (trail - 0xDC00) + 0x10000) diff --git a/test/Test/Data/String/CodePoints.purs b/test/Test/Data/String/CodePoints.purs index cd60c07..75c3573 100644 --- a/test/Test/Data/String/CodePoints.purs +++ b/test/Test/Data/String/CodePoints.purs @@ -5,6 +5,7 @@ import Prelude import Control.Monad.Eff (Eff) import Control.Monad.Eff.Console (CONSOLE, log) +import Data.Char (fromCharCode) import Data.Maybe (Maybe(..), isNothing, maybe) import Data.String.CodePoints @@ -35,6 +36,11 @@ testStringCodePoints = do assert $ codePointAt 6 str == (codePointFromInt 0x7A) assert $ codePointAt 7 str == Nothing + log "codePointFromChar" + assert $ Just (codePointFromChar 'A') == (codePointFromInt 65) + assert $ Just (codePointFromChar $ fromCharCode 0) == codePointFromInt 0 + assert $ Just (codePointFromChar $ fromCharCode 0xFFFF) == codePointFromInt 0xFFFF + log "count" assert $ count (\_ -> true) "" == 0 assert $ count (\_ -> false) str == 0 From 3b14477800a409a3e7b4a47e605a0d8a79335c80 Mon Sep 17 00:00:00 2001 From: Carsten Csiky Date: Thu, 28 Dec 2017 14:33:17 +0100 Subject: [PATCH 119/186] Add examples to docs for Data.String.CodePoints (#94) --- src/Data/String/CodePoints.purs | 150 +++++++++++++++++++++++++++++++- 1 file changed, 149 insertions(+), 1 deletion(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 1af54da..e7f8e33 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -1,7 +1,7 @@ -- | These functions allow PureScript strings to be treated as if they were -- | sequences of Unicode code points instead of their true underlying -- | implementation (sequences of UTF-16 code units). For nearly all uses of --- | strings, these functions should be preferred over the ones in Data.String. +-- | strings, these functions should be preferred over the ones in `Data.String`. module Data.String.CodePoints ( module StringReExports , CodePoint() @@ -59,10 +59,34 @@ instance showCodePoint :: Show CodePoint where -- I would prefer that this smart constructor not need to exist and instead -- CodePoint just implements Enum, but the Enum module already depends on this -- one. To avoid the circular dependency, we just expose these two functions. +-- | +-- | ```purescript +-- | >>> it = codePointFromInt 0x1D400 -- U+1D400 MATHEMATICAL BOLD CAPITAL A +-- | Just (CodePoint 0x1D400) +-- | +-- | >>> map singleton it +-- | Just "𝐀" +-- | +-- | >>> codePointFromInt 0x110000 -- does not correspond to a Unicode code point +-- | Nothing +-- | ``` +-- | codePointFromInt :: Int -> Maybe CodePoint codePointFromInt n | 0 <= n && n <= 0x10FFFF = Just (CodePoint n) codePointFromInt n = Nothing +-- | +-- | ```purescript +-- | >>> codePointToInt (codePointFromChar 'B') +-- | 66 +-- | +-- | >>> boldA = codePointFromInt 0x1D400 +-- | >>> boldA +-- | Just (CodePoint 0x1D400) +-- | >>> map codePointToInt boldA +-- | Just 119808 -- is the same as 0x1D400 +-- | ``` +-- | codePointToInt :: CodePoint -> Int codePointToInt (CodePoint n) = n @@ -109,6 +133,15 @@ unsafeCodePointAt0Fallback s = -- | Returns the first code point of the string after dropping the given number -- | of code points from the beginning, if there is such a code point. Operates -- | in constant space and in time linear to the given index. +-- | +-- | ```purescript +-- | >>> codePointAt 1 "𝐀𝐀𝐀𝐀" +-- | Just (CodePoint 0x1D400) -- represents "𝐀" +-- | -- compare to Data.String: +-- | >>> charAt 1 "𝐀𝐀𝐀𝐀" +-- | Just 'οΏ½' +-- | ``` +-- | codePointAt :: Int -> String -> Maybe CodePoint codePointAt n _ | n < 0 = Nothing codePointAt 0 "" = Nothing @@ -133,6 +166,12 @@ codePointAtFallback n s = case uncons s of -- | Returns the number of code points in the leading sequence of code points -- | which all match the given predicate. Operates in constant space and in -- | time linear to the length of the string. +-- | +-- | ```purescript +-- | >>> count (\c -> codePointToInt c == 0x1D400) "𝐀𝐀 b c 𝐀" +-- | 2 +-- | ``` +-- | count :: (CodePoint -> Boolean) -> String -> Int count = _count countFallback unsafeCodePointAt0 @@ -155,6 +194,15 @@ countTail p s accum = case uncons s of -- | Drops the given number of code points from the beginning of the string. If -- | the string does not have that many code points, returns the empty string. -- | Operates in constant space and in time linear to the given number. +-- | +-- | ```purescript +-- | >>> drop 5 "𝐀𝐀 b c" +-- | "c" +-- | -- compared to Data.String: +-- | >>> drop 5 "𝐀𝐀 b c" +-- | "b c" -- because "𝐀" occupies 2 code units +-- | ``` +-- | drop :: Int -> String -> String drop n s = String.drop (String.length (take n s)) s @@ -162,12 +210,27 @@ drop n s = String.drop (String.length (take n s)) s -- | Drops the leading sequence of code points which all match the given -- | predicate from the string. Operates in constant space and in time linear -- | to the length of the string. +-- | +-- | ```purescript +-- | >>> dropWhile (\c -> codePointToInt c == 0x1D400) "𝐀𝐀 b c 𝐀" +-- | " b c 𝐀" +-- | ``` +-- | dropWhile :: (CodePoint -> Boolean) -> String -> String dropWhile p s = drop (count p s) s -- | Creates a string from an array of code points. Operates in space and time -- | linear to the length of the array. +-- | +-- | ```purescript +-- | >>> codePointArray = toCodePointArray "c 𝐀" +-- | >>> codePointArray +-- | [CodePoint 0x63, CodePoint 0x20, CodePoint 0x1D400] +-- | >>> fromCodePointArray codePointArray +-- | "c 𝐀" +-- | ``` +-- | fromCodePointArray :: Array CodePoint -> String fromCodePointArray = _fromCodePointArray singletonFallback @@ -178,6 +241,14 @@ foreign import _fromCodePointArray -- | Returns the number of code points preceding the first match of the given -- | pattern in the string. Returns Nothing when no matches are found. +-- | +-- | ```purescript +-- | >>> indexOf (Pattern "𝐀") "b 𝐀𝐀 c 𝐀" +-- | Just 2 +-- | >>> indexOf (Pattern "o") "b 𝐀𝐀 c 𝐀" +-- | Nothing +-- | ``` +-- | indexOf :: String.Pattern -> String -> Maybe Int indexOf p s = (\i -> length (String.take i s)) <$> String.indexOf p s @@ -185,6 +256,14 @@ indexOf p s = (\i -> length (String.take i s)) <$> String.indexOf p s -- | Returns the number of code points preceding the first match of the given -- | pattern in the string. Pattern matches preceding the given index will be -- | ignored. Returns Nothing when no matches are found. +-- | +-- | ```purescript +-- | >>> indexOf' (Pattern "𝐀") 4 "b 𝐀𝐀 c 𝐀" +-- | Just 7 +-- | >>> indexOf' (Pattern "o") 4 "b 𝐀𝐀 c 𝐀" +-- | Nothing +-- | ``` +-- | indexOf' :: String.Pattern -> Int -> String -> Maybe Int indexOf' p i s = let s' = drop i s in @@ -193,6 +272,14 @@ indexOf' p i s = -- | Returns the number of code points preceding the last match of the given -- | pattern in the string. Returns Nothing when no matches are found. +-- | +-- | ```purescript +-- | >>> lastIndexOf (Pattern "𝐀") "b 𝐀𝐀 c 𝐀" +-- | Just 7 +-- | >>> lastIndexOf (Pattern "o") "b 𝐀𝐀 c 𝐀" +-- | Nothing +-- | ``` +-- | lastIndexOf :: String.Pattern -> String -> Maybe Int lastIndexOf p s = (\i -> length (String.take i s)) <$> String.lastIndexOf p s @@ -200,6 +287,14 @@ lastIndexOf p s = (\i -> length (String.take i s)) <$> String.lastIndexOf p s -- | Returns the number of code points preceding the first match of the given -- | pattern in the string. Pattern matches following the given index will be -- | ignored. Returns Nothing when no matches are found. +-- | +-- | ```purescript +-- | >>> lastIndexOf' (Pattern "𝐀") 5 "b 𝐀𝐀 c 𝐀" +-- | Just 3 +-- | >>> lastIndexOf' (Pattern "o") 5 "b 𝐀𝐀 c 𝐀" +-- | Nothing +-- | ``` +-- | lastIndexOf' :: String.Pattern -> Int -> String -> Maybe Int lastIndexOf' p i s = let i' = String.length (take i s) in @@ -208,12 +303,27 @@ lastIndexOf' p i s = -- | Returns the number of code points in the string. Operates in constant -- | space and in time linear to the length of the string. +-- | +-- | ```purescript +-- | >>> length "b 𝐀𝐀 c 𝐀" +-- | 8 +-- | -- compare to Data.String: +-- | >>> length "b 𝐀𝐀 c 𝐀" +-- | 11 +-- | ``` +-- | length :: String -> Int length = Array.length <<< toCodePointArray -- | Creates a string containing just the given code point. Operates in -- | constant space and time. +-- | +-- | ```purescript +-- | >>> map singleton (codePointFromInt 0x1D400) +-- | Just "𝐀" +-- | ``` +-- | singleton :: CodePoint -> String singleton = _singleton singletonFallback @@ -233,6 +343,12 @@ singletonFallback (CodePoint cp) = -- | 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. +-- | +-- | ```purescript +-- | >>> splitAt 3 "b 𝐀𝐀 c 𝐀" +-- | Just { before: "b 𝐀", after: "𝐀 c 𝐀" } +-- | ``` +-- | splitAt :: Int -> String -> Maybe { before :: String, after :: String } splitAt i s = let cps = toCodePointArray s in @@ -248,6 +364,15 @@ splitAt i s = -- | beginning of the given string. If the string does not have that many code -- | points, returns the empty string. Operates in constant space and in time -- | linear to the given number. +-- | +-- | ```purescript +-- | >>> take 3 "b 𝐀𝐀 c 𝐀" +-- | "b 𝐀" +-- | -- compare to Data.String: +-- | >>> take 3 "b 𝐀𝐀 c 𝐀" +-- | "b οΏ½" +-- | ``` +-- | take :: Int -> String -> String take = _take takeFallback @@ -263,12 +388,27 @@ takeFallback n s = case uncons s of -- | Returns a string containing the leading sequence of code points which all -- | match the given predicate from the string. Operates in constant space and -- | in time linear to the length of the string. +-- | +-- | ```purescript +-- | >>> takeWhile (\c -> codePointToInt c == 0x1D400) "𝐀𝐀 b c 𝐀" +-- | "𝐀𝐀" +-- | ``` +-- | takeWhile :: (CodePoint -> Boolean) -> String -> String takeWhile p s = take (count p s) s -- | Creates an array of code points from a string. Operates in space and time -- | linear to the length of the string. +-- | +-- | ```purescript +-- | >>> codePointArray = toCodePointArray "b 𝐀𝐀" +-- | >>> codePointArray +-- | [CodePoint 0x62, CodePoint 0x20, CodePoint 0x1D400, CodePoint 0x1D400] +-- | >>> map singleton codePointArray +-- | ["b", " ", "𝐀", "𝐀", " ", "c", " ", "𝐀"] +-- | ``` +-- | toCodePointArray :: String -> Array CodePoint toCodePointArray = _toCodePointArray toCodePointArrayFallback unsafeCodePointAt0 @@ -288,6 +428,14 @@ unconsButWithTuple s = (\{ head, tail } -> Tuple head tail) <$> uncons s -- | Returns a record with the first code point and the remaining code points -- | of the string. Returns Nothing if the string is empty. Operates in -- | constant space and time. +-- | +-- | ```purescript +-- | >>> uncons "𝐀𝐀 c 𝐀" +-- | Just { head: CodePoint 0x1D400, tail: "𝐀 c 𝐀" } +-- | >>> uncons "" +-- | Nothing +-- | ``` +-- | uncons :: String -> Maybe { head :: CodePoint, tail :: String } uncons s = case String.length s of 0 -> Nothing From 68e3156e8e8e223ab9c6b4da663237f29705ef76 Mon Sep 17 00:00:00 2001 From: Harry Garrood Date: Thu, 28 Dec 2017 13:40:33 +0000 Subject: [PATCH 120/186] Small fix to Data.String.CodePoints docs (#96) Remove trailing whitespace, and also fix a small error in an example --- src/Data/String/CodePoints.purs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index e7f8e33..8364670 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -62,7 +62,7 @@ instance showCodePoint :: Show CodePoint where -- | -- | ```purescript -- | >>> it = codePointFromInt 0x1D400 -- U+1D400 MATHEMATICAL BOLD CAPITAL A --- | Just (CodePoint 0x1D400) +-- | Just (CodePoint 0x1D400) -- | -- | >>> map singleton it -- | Just "𝐀" @@ -79,7 +79,7 @@ codePointFromInt n = Nothing -- | ```purescript -- | >>> codePointToInt (codePointFromChar 'B') -- | 66 --- | +-- | -- | >>> boldA = codePointFromInt 0x1D400 -- | >>> boldA -- | Just (CodePoint 0x1D400) @@ -96,7 +96,7 @@ codePointToInt (CodePoint n) = n -- | >>> codePointFromChar 'B' -- | CodePoint 0x42 -- represents 'B' -- | ``` --- | +-- | codePointFromChar :: Char -> CodePoint codePointFromChar = toCharCode >>> CodePoint @@ -202,7 +202,7 @@ countTail p s accum = case uncons s of -- | >>> drop 5 "𝐀𝐀 b c" -- | "b c" -- because "𝐀" occupies 2 code units -- | ``` --- | +-- | drop :: Int -> String -> String drop n s = String.drop (String.length (take n s)) s @@ -213,7 +213,7 @@ drop n s = String.drop (String.length (take n s)) s -- | -- | ```purescript -- | >>> dropWhile (\c -> codePointToInt c == 0x1D400) "𝐀𝐀 b c 𝐀" --- | " b c 𝐀" +-- | " b c 𝐀" -- | ``` -- | dropWhile :: (CodePoint -> Boolean) -> String -> String @@ -348,7 +348,7 @@ singletonFallback (CodePoint cp) = -- | >>> splitAt 3 "b 𝐀𝐀 c 𝐀" -- | Just { before: "b 𝐀", after: "𝐀 c 𝐀" } -- | ``` --- | +-- | splitAt :: Int -> String -> Maybe { before :: String, after :: String } splitAt i s = let cps = toCodePointArray s in @@ -372,7 +372,7 @@ splitAt i s = -- | >>> take 3 "b 𝐀𝐀 c 𝐀" -- | "b οΏ½" -- | ``` --- | +-- | take :: Int -> String -> String take = _take takeFallback @@ -391,7 +391,7 @@ takeFallback n s = case uncons s of -- | -- | ```purescript -- | >>> takeWhile (\c -> codePointToInt c == 0x1D400) "𝐀𝐀 b c 𝐀" --- | "𝐀𝐀" +-- | "𝐀𝐀" -- | ``` -- | takeWhile :: (CodePoint -> Boolean) -> String -> String @@ -406,7 +406,7 @@ takeWhile p s = take (count p s) s -- | >>> codePointArray -- | [CodePoint 0x62, CodePoint 0x20, CodePoint 0x1D400, CodePoint 0x1D400] -- | >>> map singleton codePointArray --- | ["b", " ", "𝐀", "𝐀", " ", "c", " ", "𝐀"] +-- | ["b", " ", "𝐀", "𝐀"] -- | ``` -- | toCodePointArray :: String -> Array CodePoint From 81cfa1a37bd1937c969d55a0d83d022184c86ea8 Mon Sep 17 00:00:00 2001 From: Harry Garrood Date: Thu, 28 Dec 2017 19:50:00 +0000 Subject: [PATCH 121/186] v3.4.0 From 29c7d5d3fdf9df583e6bf60f8423123134da367f Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Sun, 11 Feb 2018 18:33:45 +0000 Subject: [PATCH 122/186] Add `dropRight` / `takeRight` --- src/Data/String.purs | 24 ++++++++++++++++++++++-- test/Test/Data/String.purs | 14 ++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/Data/String.purs b/src/Data/String.purs index f927a3b..96fb2a5 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -21,8 +21,10 @@ module Data.String , replace , replaceAll , take + , takeRight , takeWhile , drop + , dropRight , dropWhile , stripPrefix , stripSuffix @@ -274,8 +276,8 @@ foreign import _lastIndexOf -> Maybe Int -- | Returns the index of the last occurrence of the pattern in the --- | given string, starting at the specified index --- | and searching backwards towards the beginning of the string. +-- | given string, starting at the specified index +-- | and searching backwards towards the beginning of the string. -- | Returns `Nothing` if there is no match. -- | -- | ```purescript @@ -347,6 +349,15 @@ foreign import replaceAll :: Pattern -> Replacement -> String -> String -- | foreign import take :: Int -> String -> String +-- | Returns the last `n` characters of the string. +-- | +-- | ```purescript +-- | take 5 "Hello World" == "World" +-- | ``` +-- | +takeRight :: Int -> String -> String +takeRight i s = drop (length s - i) s + -- | Returns the string without the first `n` characters. -- | -- | ```purescript @@ -355,6 +366,15 @@ foreign import take :: Int -> String -> String -- | foreign import drop :: Int -> String -> String +-- | Returns the string without the last `n` characters. +-- | +-- | ```purescript +-- | dropRight 6 "Hello World" == "Hello" +-- | ``` +-- | +dropRight :: Int -> String -> String +dropRight i s = take (length s - i) s + -- | Returns the number of contiguous characters at the beginning -- | of the string for which the predicate holds. -- | diff --git a/test/Test/Data/String.purs b/test/Test/Data/String.purs index 94cce8e..fa89fa6 100644 --- a/test/Test/Data/String.purs +++ b/test/Test/Data/String.purs @@ -139,6 +139,13 @@ testString = do assert $ take 3 "ab" == "ab" assert $ take (-1) "ab" == "" + log "takeRight" + assert $ takeRight 0 "ab" == "" + assert $ takeRight 1 "ab" == "b" + assert $ takeRight 2 "ab" == "ab" + assert $ takeRight 3 "ab" == "ab" + assert $ takeRight (-1) "ab" == "" + log "drop" assert $ drop 0 "ab" == "ab" assert $ drop 1 "ab" == "b" @@ -146,6 +153,13 @@ testString = do assert $ drop 3 "ab" == "" assert $ drop (-1) "ab" == "ab" + log "dropRight" + assert $ dropRight 0 "ab" == "ab" + assert $ dropRight 1 "ab" == "a" + assert $ dropRight 2 "ab" == "" + assert $ dropRight 3 "ab" == "" + assert $ dropRight (-1) "ab" == "ab" + log "count" assert $ count (_ == 'a') "" == 0 assert $ count (_ == 'a') "ab" == 1 From 2b5cae787fc1c8329f8dc7334a51f0b2f2345a7b Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Sun, 11 Feb 2018 19:45:08 +0000 Subject: [PATCH 123/186] Non-empty strings & various operations --- src/Data/String/NonEmpty.purs | 484 ++++++++++++++++++++++++++++ test/Test/Data/String/NonEmpty.purs | 254 +++++++++++++++ test/Test/Main.purs | 14 +- 3 files changed, 749 insertions(+), 3 deletions(-) create mode 100644 src/Data/String/NonEmpty.purs create mode 100644 test/Test/Data/String/NonEmpty.purs diff --git a/src/Data/String/NonEmpty.purs b/src/Data/String/NonEmpty.purs new file mode 100644 index 0000000..9281055 --- /dev/null +++ b/src/Data/String/NonEmpty.purs @@ -0,0 +1,484 @@ +-- | Non-empty strings. +-- | +-- | Please note that the examples in this documentation use a notation like +-- | `NonEmptyString "..."` for demonstration purposes, `NonEmptyString` cannot +-- | be created directly like that, as we can't prove the string is non-empty to +-- | the compiler at compile-time. +module Data.String.NonEmpty + ( NonEmptyString + , NonEmptyReplacement(..) + , fromString + , fromCharArray + , singleton + , cons + , snoc + , fromFoldable1 + , toString + , toCharArray + , charAt + , charCodeAt + , toChar + , appendString + , prependString + , contains + , indexOf + , indexOf' + , lastIndexOf + , lastIndexOf' + , uncons + , length + , localeCompare + , replace + , replaceAll + , take + , takeRight + , takeWhile + , drop + , dropRight + , dropWhile + , stripPrefix + , stripSuffix + , count + , splitAt + , toLower + , toUpper + , trim + , joinWith + , join1With + , joinWith1 + , module Data.String + ) where + +import Prelude + +import Data.Foldable (class Foldable) +import Data.Foldable as F +import Data.Maybe (Maybe(..)) +import Data.Semigroup.Foldable (class Foldable1) +import Data.Semigroup.Foldable as F1 +import Data.String (Pattern(..)) +import Data.String as String +import Data.String.Unsafe as U +import Unsafe.Coerce (unsafeCoerce) + +-- | A string that is known not to be empty. +newtype NonEmptyString = NonEmptyString String + +derive newtype instance eqNonEmptyString ∷ Eq NonEmptyString +derive newtype instance ordNonEmptyString ∷ Ord NonEmptyString +derive newtype instance semigroupNonEmptyString ∷ Semigroup NonEmptyString + +instance showNonEmptyString :: Show NonEmptyString where + show (NonEmptyString s) = "(NonEmptyString.fromString " <> show s <> ")" + +-- | A newtype used in cases to specify a non-empty replacement for a pattern. +newtype NonEmptyReplacement = NonEmptyReplacement NonEmptyString + +derive newtype instance eqNonEmptyReplacement :: Eq NonEmptyReplacement +derive newtype instance ordNonEmptyReplacement :: Ord NonEmptyReplacement +derive newtype instance semigroupNonEmptyReplacement ∷ Semigroup NonEmptyReplacement + +instance showNonEmptyReplacement :: Show NonEmptyReplacement where + show (NonEmptyReplacement s) = "(NonEmptyReplacement " <> show s <> ")" + +-- | Creates a `NonEmptyString` from a `String`, returning `Nothing` if the +-- | input is empty. +-- | +-- | ```purescript +-- | fromString "" = Nothing +-- | fromString "hello" = Just (NonEmptyString "hello") +-- | ``` +fromString :: String -> Maybe NonEmptyString +fromString = case _ of + "" -> Nothing + s -> Just (NonEmptyString s) + +-- | Creates a `NonEmptyString` from a character array `String`, returning +-- | `Nothing` if the input is empty. +-- | +-- | ```purescript +-- | fromCharArray [] = Nothing +-- | fromCharArray ['a', 'b', 'c'] = Just (NonEmptyString "abc") +-- | ``` +fromCharArray :: Array Char -> Maybe NonEmptyString +fromCharArray = case _ of + [] -> Nothing + cs -> Just (NonEmptyString (String.fromCharArray cs)) + +-- | Creates a `NonEmptyString` from a character. +singleton :: Char -> NonEmptyString +singleton = NonEmptyString <<< String.singleton + +-- | Creates a `NonEmptyString` from a string by prepending a character. +-- | +-- | ```purescript +-- | cons 'a' "bc" = NonEmptyString "abc" +-- | cons 'a' "" = NonEmptyString "a" +-- | ``` +cons :: Char -> String -> NonEmptyString +cons c s = NonEmptyString (String.singleton c <> s) + +-- | Creates a `NonEmptyString` from a string by appending a character. +-- | +-- | ```purescript +-- | snoc 'c' "ab" = NonEmptyString "abc" +-- | snoc 'a' "" = NonEmptyString "a" +-- | ``` +snoc :: Char -> String -> NonEmptyString +snoc c s = NonEmptyString (s <> String.singleton c) + +-- | Creates a `NonEmptyString` from a `Foldable1` container carrying +-- | characters. +fromFoldable1 :: forall f. Foldable1 f => f Char -> NonEmptyString +fromFoldable1 = F1.fold1 <<< coe + where + coe ∷ f Char -> f NonEmptyString + coe = unsafeCoerce + +-- | Converts a `NonEmptyString` back into a standard `String`. +toString :: NonEmptyString -> String +toString (NonEmptyString s) = s + +-- | Returns the character at the given index, if the index is within bounds. +-- | +-- | ```purescript +-- | charAt 2 (NonEmptyString "Hello") == Just 'l' +-- | charAt 10 (NonEmptyString "Hello") == Nothing +-- | ``` +charAt :: Int -> NonEmptyString -> Maybe Char +charAt = liftS <<< String.charAt + +-- | Returns the numeric Unicode value of the character at the given index, +-- | if the index is within bounds. +-- | +-- | ```purescript +-- | charCodeAt 2 (NonEmptyString "5 €") == Just 0x20AC +-- | charCodeAt 10 (NonEmptyString "5 €") == Nothing +-- | ``` +charCodeAt :: Int -> NonEmptyString -> Maybe Int +charCodeAt = liftS <<< String.charCodeAt + +-- | Converts the `NonEmptyString` to a character, if the length of the string +-- | is exactly `1`. +-- | +-- | ```purescript +-- | toChar "H" == Just 'H' +-- | toChar "Hi" == Nothing +-- | ``` +toChar :: NonEmptyString -> Maybe Char +toChar (NonEmptyString s) = String.toChar s + +-- | Converts the `NonEmptyString` into an array of characters. +-- | +-- | ```purescript +-- | toCharArray (NonEmptyString "Hello☺\n") == ['H','e','l','l','o','☺','\n'] +-- | ``` +toCharArray :: NonEmptyString -> Array Char +toCharArray (NonEmptyString s) = String.toCharArray s + +-- | Appends a string to this non-empty string. Since one of the strings is +-- | non-empty we know the result will be too. +-- | +-- | ```purescript +-- | appendString (NonEmptyString "Hello") " world" == NonEmptyString "Hello world" +-- | appendString (NonEmptyString "Hello") "" == NonEmptyString "Hello" +-- | ``` +appendString :: NonEmptyString -> String -> NonEmptyString +appendString (NonEmptyString s1) s2 = NonEmptyString (s1 <> s2) + +-- | Prepends a string to this non-empty string. Since one of the strings is +-- | non-empty we know the result will be too. +-- | +-- | ```purescript +-- | prependString "be" (NonEmptyString "fore") == NonEmptyString "before" +-- | prependString "" (NonEmptyString "fore") == NonEmptyString "fore" +-- | ``` +prependString :: String -> NonEmptyString -> NonEmptyString +prependString s1 (NonEmptyString s2) = NonEmptyString (s1 <> s2) + +-- | Returns the first character and the rest of the string. +-- | +-- | ```purescript +-- | uncons "a" == { head: 'a', tail: Nothing } +-- | uncons "Hello World" == { head: 'H', tail: Just (NonEmptyString "ello World") } +-- | ``` +uncons :: NonEmptyString -> { head :: Char, tail :: Maybe NonEmptyString } +uncons (NonEmptyString s) = + { head: U.charAt 0 s + , tail: fromString (String.drop 1 s) + } + +-- | Returns the longest prefix of characters that satisfy the predicate. +-- | `Nothing` is returned if there is no matching prefix. +-- | +-- | ```purescript +-- | takeWhile (_ /= ':') (NonEmptyString "http://purescript.org") == Just (NonEmptyString "http") +-- | takeWhile (_ == 'a') (NonEmptyString "xyz") == Nothing +-- | ``` +takeWhile :: (Char -> Boolean) -> NonEmptyString -> Maybe NonEmptyString +takeWhile f = fromString <<< liftS (String.takeWhile f) + +-- | Returns the suffix remaining after `takeWhile`. +-- | +-- | ```purescript +-- | dropWhile (_ /= '.') (NonEmptyString "Test.purs") == Just (NonEmptyString ".purs") +-- | ``` +dropWhile :: (Char -> Boolean) -> NonEmptyString -> Maybe NonEmptyString +dropWhile f = fromString <<< liftS (String.dropWhile f) + +-- | If the string starts with the given prefix, return the portion of the +-- | string left after removing it. If the prefix does not match or there is no +-- | remainder, the result will be `Nothing`. +-- | +-- | ```purescript +-- | stripPrefix (Pattern "http:") (NonEmptyString "http://purescript.org") == Just (NonEmptyString "//purescript.org") +-- | stripPrefix (Pattern "http:") (NonEmptyString "https://purescript.org") == Nothing +-- | stripPrefix (Pattern "Hello!") (NonEmptyString "Hello!") == Nothing +-- | ``` +stripPrefix :: Pattern -> NonEmptyString -> Maybe NonEmptyString +stripPrefix pat = fromString <=< liftS (String.stripPrefix pat) + +-- | If the string ends with the given suffix, return the portion of the +-- | string left after removing it. If the suffix does not match or there is no +-- | remainder, the result will be `Nothing`. +-- | +-- | ```purescript +-- | stripSuffix (Pattern ".exe") (NonEmptyString "purs.exe") == Just (NonEmptyString "purs") +-- | stripSuffix (Pattern ".exe") (NonEmptyString "purs") == Nothing +-- | stripSuffix (Pattern "Hello!") (NonEmptyString "Hello!") == Nothing +-- | ``` +stripSuffix :: Pattern -> NonEmptyString -> Maybe NonEmptyString +stripSuffix pat = fromString <=< liftS (String.stripSuffix pat) + +-- | Checks whether the pattern appears in the given string. +-- | +-- | ```purescript +-- | contains (Pattern "needle") (NonEmptyString "haystack with needle") == true +-- | contains (Pattern "needle") (NonEmptyString "haystack") == false +-- | ``` +contains :: Pattern -> NonEmptyString -> Boolean +contains = liftS <<< String.contains + +-- | Returns the index of the first occurrence of the pattern in the +-- | given string. Returns `Nothing` if there is no match. +-- | +-- | ```purescript +-- | indexOf (Pattern "c") (NonEmptyString "abcdc") == Just 2 +-- | indexOf (Pattern "c") (NonEmptyString "aaa") == Nothing +-- | ``` +indexOf :: Pattern -> NonEmptyString -> Maybe Int +indexOf = liftS <<< String.indexOf + +-- | Returns the index of the first occurrence of the pattern in the +-- | given string, starting at the specified index. Returns `Nothing` if there is +-- | no match. +-- | +-- | ```purescript +-- | indexOf' (Pattern "a") 2 (NonEmptyString "ababa") == Just 2 +-- | indexOf' (Pattern "a") 3 (NonEmptyString "ababa") == Just 4 +-- | ``` +indexOf' :: Pattern -> Int -> NonEmptyString -> Maybe Int +indexOf' pat = liftS <<< String.indexOf' pat + +-- | Returns the index of the last occurrence of the pattern in the +-- | given string. Returns `Nothing` if there is no match. +-- | +-- | ```purescript +-- | lastIndexOf (Pattern "c") (NonEmptyString "abcdc") == Just 4 +-- | lastIndexOf (Pattern "c") (NonEmptyString "aaa") == Nothing +-- | ``` +lastIndexOf :: Pattern -> NonEmptyString -> Maybe Int +lastIndexOf = liftS <<< String.lastIndexOf + +-- | Returns the index of the last occurrence of the pattern in the +-- | given string, starting at the specified index +-- | and searching backwards towards the beginning of the string. +-- | Returns `Nothing` if there is no match. +-- | +-- | ```purescript +-- | lastIndexOf' (Pattern "a") 1 (NonEmptyString "ababa") == Just 0 +-- | lastIndexOf' (Pattern "a") 3 (NonEmptyString "ababa") == Just 2 +-- | lastIndexOf' (Pattern "a") 4 (NonEmptyString "ababa") == Just 4 +-- | ``` +lastIndexOf' :: Pattern -> Int -> NonEmptyString -> Maybe Int +lastIndexOf' pat = liftS <<< String.lastIndexOf' pat + +-- | Returns the number of characters the string is composed of. +-- | +-- | ```purescript +-- | length (NonEmptyString "Hello World") == 11 +-- | ``` +length :: NonEmptyString -> Int +length (NonEmptyString s) = String.length s + +-- | Compare two strings in a locale-aware fashion. This is in contrast to +-- | the `Ord` instance on `String` which treats strings as arrays of code +-- | units: +-- | +-- | ```purescript +-- | NonEmptyString "Γ€" `localeCompare` NonEmptyString "b" == LT +-- | NonEmptyString "Γ€" `compare` NonEmptyString "b" == GT +-- | ``` +localeCompare :: NonEmptyString -> NonEmptyString -> Ordering +localeCompare (NonEmptyString a) (NonEmptyString b) = String.localeCompare a b + +-- | Replaces the first occurence of the pattern with the replacement string. +-- | +-- | ```purescript +-- | replace (Pattern "<=") (NonEmptyReplacement "≀") (NonEmptyString "a <= b <= c") == NonEmptyString "a ≀ b <= c" +-- | ``` +replace :: Pattern -> NonEmptyReplacement -> NonEmptyString -> NonEmptyString +replace pat (NonEmptyReplacement (NonEmptyString rep)) (NonEmptyString s) = + NonEmptyString (String.replace pat (String.Replacement rep) s) + +-- | Replaces all occurences of the pattern with the replacement string. +-- | +-- | ```purescript +-- | replaceAll (Pattern "<=") (NonEmptyReplacement "≀") (NonEmptyString "a <= b <= c") == NonEmptyString "a ≀ b ≀ c" +-- | ``` +replaceAll :: Pattern -> NonEmptyReplacement -> NonEmptyString -> NonEmptyString +replaceAll pat (NonEmptyReplacement (NonEmptyString rep)) (NonEmptyString s) = + NonEmptyString (String.replaceAll pat (String.Replacement rep) s) + +-- | Returns the first `n` characters of the string. Returns `Nothing` if `n` is +-- | less than 1. +-- | +-- | ```purescript +-- | take 5 (NonEmptyString "Hello World") == Just (NonEmptyString "Hello") +-- | take 0 (NonEmptyString "Hello World") == Nothing +-- | ``` +take :: Int -> NonEmptyString -> Maybe NonEmptyString +take i (NonEmptyString s) + | i < 1 = Nothing + | otherwise = Just (NonEmptyString (String.take i s)) + +-- | Returns the last `n` characters of the string. Returns `Nothing` if `n` is +-- | less than 1. +-- | +-- | ```purescript +-- | take 5 (NonEmptyString "Hello World") == Just (NonEmptyString "World") +-- | take 0 (NonEmptyString "Hello World") == Nothing +-- | ``` +takeRight :: Int -> NonEmptyString -> Maybe NonEmptyString +takeRight i (NonEmptyString s) + | i < 1 = Nothing + | otherwise = Just (NonEmptyString (String.takeRight i s)) + +-- | Returns the string without the first `n` characters. Returns `Nothing` if +-- | more characters are dropped than the string is long. +-- | +-- | ```purescript +-- | drop 6 (NonEmptyString "Hello World") == Just (NonEmptyString "World") +-- | drop 20 (NonEmptyString "Hello World") == Nothing +-- | ``` +drop :: Int -> NonEmptyString -> Maybe NonEmptyString +drop i (NonEmptyString s) + | i >= String.length s = Nothing + | otherwise = Just (NonEmptyString (String.drop i s)) + +-- | Returns the string without the last `n` characters. Returns `Nothing` if +-- | more characters are dropped than the string is long. +-- | +-- | ```purescript +-- | dropRight 6 (NonEmptyString "Hello World") == Just (NonEmptyString "Hello") +-- | dropRight 20 (NonEmptyString "Hello World") == Nothing +-- | ``` +dropRight :: Int -> NonEmptyString -> Maybe NonEmptyString +dropRight i (NonEmptyString s) + | i >= String.length s = Nothing + | otherwise = Just (NonEmptyString (String.dropRight i s)) + +-- | Returns the number of contiguous characters at the beginning of the string +-- | for which the predicate holds. +-- | +-- | ```purescript +-- | count (_ /= 'o') (NonEmptyString "Hello World") == 4 +-- | ``` +count :: (Char -> Boolean) -> NonEmptyString -> Int +count = liftS <<< String.count + +-- | Returns the substrings of a split at the given index, if the index is +-- | within bounds. +-- | +-- | ```purescript +-- | splitAt 2 (NonEmptyString "Hello World") == Just { before: Just (NonEmptyString "He"), after: Just (NonEmptyString "llo World") } +-- | splitAt 10 (NonEmptyString "Hi") == Nothing +-- | ``` +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 + +-- | Returns the argument converted to lowercase. +-- | +-- | ```purescript +-- | toLower (NonEmptyString "hElLo") == NonEmptyString "hello" +-- | ``` +toLower :: NonEmptyString -> NonEmptyString +toLower (NonEmptyString s) = NonEmptyString (String.toLower s) + +-- | Returns the argument converted to uppercase. +-- | +-- | ```purescript +-- | toUpper (NonEmptyString "Hello") == NonEmptyString "HELLO" +-- | ``` +toUpper :: NonEmptyString -> NonEmptyString +toUpper (NonEmptyString s) = NonEmptyString (String.toUpper s) + +-- | Removes whitespace from the beginning and end of a string, including +-- | [whitespace characters](http://www.ecma-international.org/ecma-262/5.1/#sec-7.2) +-- | and [line terminators](http://www.ecma-international.org/ecma-262/5.1/#sec-7.3). +-- | If the string is entirely made up of whitespace the result will be Nothing. +-- | +-- | ```purescript +-- | trim (NonEmptyString " Hello \n World\n\t ") == Just (NonEmptyString "Hello \n World") +-- | trim (NonEmptyString " \n") == Nothing +-- | ``` +trim :: NonEmptyString -> Maybe NonEmptyString +trim (NonEmptyString s) = fromString (String.trim s) + +-- | Joins the strings in a container together as a new string, inserting the +-- | first argument as separator between them. The result is not guaranteed to +-- | be non-empty. +-- | +-- | ```purescript +-- | joinWith ", " [NonEmptyString "apple", NonEmptyString "banana"] == "apple, banana" +-- | joinWith ", " [] == "" +-- | ``` +joinWith :: forall f. Foldable f => String -> f NonEmptyString -> String +joinWith splice = F.intercalate splice <<< coe + where + coe :: f NonEmptyString -> f String + coe = unsafeCoerce + +-- | Joins non-empty strings in a non-empty container together as a new +-- | non-empty string, inserting a possibly empty string as separator between +-- | them. The result is guaranteed to be non-empty. +-- | +-- | ```purescript +-- | -- array syntax is used for demonstration here, it would need to be a real `Foldable1` +-- | join1With ", " [NonEmptyString "apple", NonEmptyString "banana"] == NonEmptyString "apple, banana" +-- | join1With "" [NonEmptyString "apple", NonEmptyString "banana"] == NonEmptyString "applebanana" +-- | ``` +join1With :: forall f. Foldable1 f => String -> f NonEmptyString -> NonEmptyString +join1With splice = NonEmptyString <<< joinWith splice + +-- | Joins possibly empty strings in a non-empty container together as a new +-- | non-empty string, inserting a non-empty string as a separator between them. +-- | The result is guaranteed to be non-empty. +-- | +-- | ```purescript +-- | -- array syntax is used for demonstration here, it would need to be a real `Foldable1` +-- | joinWith1 (NonEmptyString ", ") ["apple", "banana"] == NonEmptyString "apple, banana" +-- | joinWith1 (NonEmptyString "/") ["a", "b", "", "c", ""] == NonEmptyString "a/b//c/" +-- | ``` +joinWith1 :: forall f. Foldable1 f => NonEmptyString -> f String -> NonEmptyString +joinWith1 (NonEmptyString splice) = NonEmptyString <<< F.intercalate splice + +liftS :: forall r. (String -> r) -> NonEmptyString -> r +liftS f (NonEmptyString s) = f s diff --git a/test/Test/Data/String/NonEmpty.purs b/test/Test/Data/String/NonEmpty.purs new file mode 100644 index 0000000..63c5d50 --- /dev/null +++ b/test/Test/Data/String/NonEmpty.purs @@ -0,0 +1,254 @@ +module Test.Data.String.NonEmpty (testNonEmptyString) where + +import Data.String.NonEmpty + +import Control.Monad.Eff (Eff) +import Control.Monad.Eff.Console (CONSOLE, log) +import Data.Array.Partial as AP +import Data.Foldable (class Foldable, foldl) +import Data.Maybe (Maybe(..), fromJust, isNothing, maybe) +import Data.Semigroup.Foldable (class Foldable1, foldMap1Default) +import Partial.Unsafe (unsafePartial) +import Prelude (class Functor, Ordering(..), Unit, append, discard, negate, not, ($), (&&), (/=), (==)) +import Test.Assert (ASSERT, assert) + +testNonEmptyString :: forall eff. Eff (console :: CONSOLE, assert :: ASSERT | eff) Unit +testNonEmptyString = do + log "fromString" + assert $ fromString "" == Nothing + assert $ fromString "hello" == Just (nes "hello") + + log "fromCharArray" + assert $ fromCharArray [] == Nothing + assert $ fromCharArray ['a', 'b'] == Just (nes "ab") + + log "singleton" + assert $ singleton 'a' == nes "a" + + log "cons" + assert $ cons 'a' "bc" == nes "abc" + assert $ cons 'a' "" == nes "a" + + log "snoc" + assert $ snoc 'c' "ab" == nes "abc" + assert $ snoc 'a' "" == nes "a" + + log "fromFoldable1" + assert $ fromFoldable1 (NEA ['a']) == nes "a" + assert $ fromFoldable1 (NEA ['a', 'b', 'c']) == nes "abc" + + log "charAt" + assert $ charAt 0 (nes "a") == Just 'a' + assert $ charAt 1 (nes "a") == Nothing + assert $ charAt 0 (nes "ab") == Just 'a' + assert $ charAt 1 (nes "ab") == Just 'b' + assert $ charAt 2 (nes "ab") == Nothing + assert $ charAt 2 (nes "Hello") == Just 'l' + assert $ charAt 10 (nes "Hello") == Nothing + + log "charCodeAt" + assert $ charCodeAt 0 (nes "a") == Just 97 + assert $ charCodeAt 1 (nes "a") == Nothing + assert $ charCodeAt 0 (nes "ab") == Just 97 + assert $ charCodeAt 1 (nes "ab") == Just 98 + assert $ charCodeAt 2 (nes "ab") == Nothing + assert $ charCodeAt 2 (nes "5 €") == Just 0x20AC + assert $ charCodeAt 10 (nes "5 €") == Nothing + + log "toChar" + assert $ toChar (nes "a") == Just 'a' + assert $ toChar (nes "ab") == Nothing + + log "toCharArray" + assert $ toCharArray (nes "a") == ['a'] + assert $ toCharArray (nes "ab") == ['a', 'b'] + assert $ toCharArray (nes "Hello☺\n") == ['H','e','l','l','o','☺','\n'] + + log "appendString" + assert $ appendString (nes "Hello") " world" == nes "Hello world" + assert $ appendString (nes "Hello") "" == nes "Hello" + + log "prependString" + assert $ prependString "be" (nes "fore") == nes "before" + assert $ prependString "" (nes "fore") == nes "fore" + + log "uncons" + assert + let m = uncons (nes "a") + in m.head == 'a' && m.tail == Nothing + assert $ + let m = uncons (nes "Hello World") + in m.head == 'H' && m.tail == Just (nes "ello World") + + log "takeWhile" + assert $ takeWhile (\c -> true) (nes "abc") == Just (nes "abc") + assert $ takeWhile (\c -> false) (nes "abc") == Nothing + assert $ takeWhile (\c -> c /= 'b') (nes "aabbcc") == Just (nes "aa") + assert $ takeWhile (_ /= ':') (nes "http://purescript.org") == Just (nes "http") + assert $ takeWhile (_ == 'a') (nes "xyz") == Nothing + + log "dropWhile" + assert $ dropWhile (\c -> true) (nes "abc") == Nothing + assert $ dropWhile (\c -> false) (nes "abc") == Just (nes "abc") + assert $ dropWhile (\c -> c /= 'b') (nes "aabbcc") == Just (nes "bbcc") + assert $ dropWhile (_ /= '.') (nes "Test.purs") == Just (nes ".purs") + + log "stripPrefix" + assert $ stripPrefix (Pattern "") (nes "abc") == Just (nes "abc") + assert $ stripPrefix (Pattern "a") (nes "abc") == Just (nes "bc") + assert $ stripPrefix (Pattern "abc") (nes "abc") == Nothing + assert $ stripPrefix (Pattern "!") (nes "abc") == Nothing + assert $ stripPrefix (Pattern "http:") (nes "http://purescript.org") == Just (nes "//purescript.org") + assert $ stripPrefix (Pattern "http:") (nes "https://purescript.org") == Nothing + assert $ stripPrefix (Pattern "Hello!") (nes "Hello!") == Nothing + + log "stripSuffix" + assert $ stripSuffix (Pattern ".exe") (nes "purs.exe") == Just (nes "purs") + assert $ stripSuffix (Pattern ".exe") (nes "purs") == Nothing + assert $ stripSuffix (Pattern "Hello!") (nes "Hello!") == Nothing + + log "contains" + assert $ contains (Pattern "") (nes "abcd") + assert $ contains (Pattern "bc") (nes "abcd") + assert $ not (contains (Pattern "cb") (nes "abcd")) + assert $ contains (Pattern "needle") (nes "haystack with needle") == true + assert $ contains (Pattern "needle") (nes "haystack") == false + + log "indexOf" + assert $ indexOf (Pattern "") (nes "abcd") == Just 0 + assert $ indexOf (Pattern "bc") (nes "abcd") == Just 1 + assert $ indexOf (Pattern "cb") (nes "abcd") == Nothing + + log "indexOf'" + assert $ indexOf' (Pattern "") (-1) (nes "ab") == Nothing + assert $ indexOf' (Pattern "") 0 (nes "ab") == Just 0 + assert $ indexOf' (Pattern "") 1 (nes "ab") == Just 1 + assert $ indexOf' (Pattern "") 2 (nes "ab") == Just 2 + assert $ indexOf' (Pattern "") 3 (nes "ab") == Nothing + assert $ indexOf' (Pattern "bc") 0 (nes "abcd") == Just 1 + assert $ indexOf' (Pattern "bc") 1 (nes "abcd") == Just 1 + assert $ indexOf' (Pattern "bc") 2 (nes "abcd") == Nothing + assert $ indexOf' (Pattern "cb") 0 (nes "abcd") == Nothing + + log "lastIndexOf" + assert $ lastIndexOf (Pattern "") (nes "abcd") == Just 4 + assert $ lastIndexOf (Pattern "bc") (nes "abcd") == Just 1 + assert $ lastIndexOf (Pattern "cb") (nes "abcd") == Nothing + + log "lastIndexOf'" + assert $ lastIndexOf' (Pattern "") (-1) (nes "ab") == Nothing + assert $ lastIndexOf' (Pattern "") 0 (nes "ab") == Just 0 + assert $ lastIndexOf' (Pattern "") 1 (nes "ab") == Just 1 + assert $ lastIndexOf' (Pattern "") 2 (nes "ab") == Just 2 + assert $ lastIndexOf' (Pattern "") 3 (nes "ab") == Nothing + assert $ lastIndexOf' (Pattern "bc") 0 (nes "abcd") == Nothing + assert $ lastIndexOf' (Pattern "bc") 1 (nes "abcd") == Just 1 + assert $ lastIndexOf' (Pattern "bc") 2 (nes "abcd") == Just 1 + assert $ lastIndexOf' (Pattern "cb") 0 (nes "abcd") == Nothing + + log "length" + assert $ length (nes "a") == 1 + assert $ length (nes "ab") == 2 + + log "localeCompare" + assert $ localeCompare (nes "a") (nes "a") == EQ + assert $ localeCompare (nes "a") (nes "b") == LT + assert $ localeCompare (nes "b") (nes "a") == GT + + log "replace" + assert $ replace (Pattern "b") (NonEmptyReplacement (nes "!")) (nes "abc") == nes "a!c" + assert $ replace (Pattern "b") (NonEmptyReplacement (nes "!")) (nes "abbc") == nes "a!bc" + assert $ replace (Pattern "d") (NonEmptyReplacement (nes "!")) (nes "abc") == nes "abc" + + log "replaceAll" + assert $ replaceAll (Pattern "[b]") (NonEmptyReplacement (nes "!")) (nes "a[b]c") == nes "a!c" + assert $ replaceAll (Pattern "[b]") (NonEmptyReplacement (nes "!")) (nes "a[b]c[b]") == nes "a!c!" + assert $ replaceAll (Pattern "x") (NonEmptyReplacement (nes "!")) (nes "abc") == nes "abc" + + log "take" + assert $ take 0 (nes "ab") == Nothing + assert $ take 1 (nes "ab") == Just (nes "a") + assert $ take 2 (nes "ab") == Just (nes "ab") + assert $ take 3 (nes "ab") == Just (nes "ab") + assert $ take (-1) (nes "ab") == Nothing + + log "takeRight" + assert $ takeRight 0 (nes "ab") == Nothing + assert $ takeRight 1 (nes "ab") == Just (nes "b") + assert $ takeRight 2 (nes "ab") == Just (nes "ab") + assert $ takeRight 3 (nes "ab") == Just (nes "ab") + assert $ takeRight (-1) (nes "ab") == Nothing + + log "drop" + assert $ drop 0 (nes "ab") == Just (nes "ab") + assert $ drop 1 (nes "ab") == Just (nes "b") + assert $ drop 2 (nes "ab") == Nothing + assert $ drop 3 (nes "ab") == Nothing + assert $ drop (-1) (nes "ab") == Just (nes "ab") + + log "dropRight" + assert $ dropRight 0 (nes "ab") == Just (nes "ab") + assert $ dropRight 1 (nes "ab") == Just (nes "a") + assert $ dropRight 2 (nes "ab") == Nothing + assert $ dropRight 3 (nes "ab") == Nothing + assert $ dropRight (-1) (nes "ab") == Just (nes "ab") + + log "count" + assert $ count (_ == 'a') (nes "ab") == 1 + assert $ count (_ == 'a') (nes "aaab") == 3 + assert $ count (_ == 'a') (nes "abaa") == 1 + assert $ count (_ == 'c') (nes "abaa") == 0 + + log "splitAt" + 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 + + log "toLower" + assert $ toLower (nes "bAtMaN") == nes "batman" + + log "toUpper" + assert $ toUpper (nes "bAtMaN") == nes "BATMAN" + + log "trim" + assert $ trim (nes " abc ") == Just (nes "abc") + assert $ trim (nes " \n") == Nothing + + log "joinWith" + assert $ joinWith "" [] == "" + assert $ joinWith "" [nes "a", nes "b"] == "ab" + assert $ joinWith "--" [nes "a", nes "b", nes "c"] == "a--b--c" + + log "join1With" + assert $ join1With "" (NEA [nes "a", nes "b"]) == nes "ab" + assert $ join1With "--" (NEA [nes "a", nes "b", nes "c"]) == nes "a--b--c" + assert $ join1With ", " (NEA [nes "apple", nes "banana"]) == nes "apple, banana" + assert $ join1With "" (NEA [nes "apple", nes "banana"]) == nes "applebanana" + + log "joinWith1" + assert $ joinWith1 (nes " ") (NEA ["a", "b"]) == nes "a b" + assert $ joinWith1 (nes "--") (NEA ["a", "b", "c"]) == nes "a--b--c" + assert $ joinWith1 (nes ", ") (NEA ["apple", "banana"]) == nes "apple, banana" + assert $ joinWith1 (nes "/") (NEA ["a", "b", "", "c", ""]) == nes "a/b//c/" + + +nes :: String -> NonEmptyString +nes s = unsafePartial (fromJust (fromString s)) + +newtype NEA a = NEA (Array a) + +derive newtype instance functorNEA :: Functor NEA +derive newtype instance foldableNEA :: Foldable NEA + +instance foldable1NEA :: Foldable1 NEA where + foldMap1 a = foldMap1Default a + fold1 (NEA as) = foldl append (unsafePartial AP.head as) (unsafePartial AP.tail as) diff --git a/test/Test/Main.purs b/test/Test/Main.purs index 8260cc6..e81600f 100644 --- a/test/Test/Main.purs +++ b/test/Test/Main.purs @@ -3,21 +3,29 @@ module Test.Main where import Prelude import Control.Monad.Eff (Eff) -import Control.Monad.Eff.Console (CONSOLE) - +import Control.Monad.Eff.Console (CONSOLE, log) import Test.Assert (ASSERT) import Test.Data.Char (testChar) import Test.Data.String (testString) +import Test.Data.String.CaseInsensitive (testCaseInsensitiveString) import Test.Data.String.CodePoints (testStringCodePoints) +import Test.Data.String.NonEmpty (testNonEmptyString) import Test.Data.String.Regex (testStringRegex) import Test.Data.String.Unsafe (testStringUnsafe) -import Test.Data.String.CaseInsensitive (testCaseInsensitiveString) main :: Eff (console :: CONSOLE, assert :: ASSERT) Unit main = do + log "\n--- Data.Char ---\n" testChar + log "\n--- Data.String ---\n" testString + log "\n--- Data.String.CodePoints ---\n" testStringCodePoints + log "\n--- Data.String.Unsafe ---\n" testStringUnsafe + log "\n--- Data.String.Regex ---\n" testStringRegex + log "\n--- Data.String.CaseInsensitive ---\n" testCaseInsensitiveString + log "\n--- Data.String.NonEmpty ---\n" + testNonEmptyString From 48c77566361307641c1f696cb4c0bb4c93a4434b Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Mon, 12 Feb 2018 17:24:58 +0000 Subject: [PATCH 124/186] Add `unsafeFromString` partial function --- src/Data/String/NonEmpty.purs | 9 +++++++-- test/Test/Data/String/NonEmpty.purs | 3 +-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Data/String/NonEmpty.purs b/src/Data/String/NonEmpty.purs index 9281055..b3d93a9 100644 --- a/src/Data/String/NonEmpty.purs +++ b/src/Data/String/NonEmpty.purs @@ -8,6 +8,7 @@ module Data.String.NonEmpty ( NonEmptyString , NonEmptyReplacement(..) , fromString + , unsafeFromString , fromCharArray , singleton , cons @@ -53,7 +54,7 @@ import Prelude import Data.Foldable (class Foldable) import Data.Foldable as F -import Data.Maybe (Maybe(..)) +import Data.Maybe (Maybe(..), fromJust) import Data.Semigroup.Foldable (class Foldable1) import Data.Semigroup.Foldable as F1 import Data.String (Pattern(..)) @@ -69,7 +70,7 @@ derive newtype instance ordNonEmptyString ∷ Ord NonEmptyString derive newtype instance semigroupNonEmptyString ∷ Semigroup NonEmptyString instance showNonEmptyString :: Show NonEmptyString where - show (NonEmptyString s) = "(NonEmptyString.fromString " <> show s <> ")" + show (NonEmptyString s) = "(NonEmptyString.unsafeFromString " <> show s <> ")" -- | A newtype used in cases to specify a non-empty replacement for a pattern. newtype NonEmptyReplacement = NonEmptyReplacement NonEmptyString @@ -93,6 +94,10 @@ fromString = case _ of "" -> Nothing s -> Just (NonEmptyString s) +-- | A partial version of `fromString`. +unsafeFromString :: Partial => String -> NonEmptyString +unsafeFromString = fromJust <<< fromString + -- | Creates a `NonEmptyString` from a character array `String`, returning -- | `Nothing` if the input is empty. -- | diff --git a/test/Test/Data/String/NonEmpty.purs b/test/Test/Data/String/NonEmpty.purs index 63c5d50..fc80784 100644 --- a/test/Test/Data/String/NonEmpty.purs +++ b/test/Test/Data/String/NonEmpty.purs @@ -240,9 +240,8 @@ testNonEmptyString = do assert $ joinWith1 (nes ", ") (NEA ["apple", "banana"]) == nes "apple, banana" assert $ joinWith1 (nes "/") (NEA ["a", "b", "", "c", ""]) == nes "a/b//c/" - nes :: String -> NonEmptyString -nes s = unsafePartial (fromJust (fromString s)) +nes = unsafePartial unsafeFromString newtype NEA a = NEA (Array a) From 06e048c1a6026e3fd6dd9aff0a2d26da23cec783 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Mon, 12 Feb 2018 17:30:41 +0000 Subject: [PATCH 125/186] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 7050558..332b6cf 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ /bower_components/ /node_modules/ /output/ +package-lock.json From 1b32a5c8ccd65a5629a6631313e992f0e573231b Mon Sep 17 00:00:00 2001 From: Irakli Safareli Date: Tue, 13 Mar 2018 11:07:23 +0100 Subject: [PATCH 126/186] fix takeRight example --- src/Data/String.purs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Data/String.purs b/src/Data/String.purs index 96fb2a5..e9d5088 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -352,7 +352,7 @@ foreign import take :: Int -> String -> String -- | Returns the last `n` characters of the string. -- | -- | ```purescript --- | take 5 "Hello World" == "World" +-- | takeRight 5 "Hello World" == "World" -- | ``` -- | takeRight :: Int -> String -> String From e7519dd8be96405a686bbd39be4161141a9ae206 Mon Sep 17 00:00:00 2001 From: Matthew Chan Date: Tue, 20 Mar 2018 15:07:35 -0700 Subject: [PATCH 127/186] add slice --- src/Data/String.js | 8 ++++++++ src/Data/String.purs | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/Data/String.js b/src/Data/String.js index c06bfcd..76f8895 100644 --- a/src/Data/String.js +++ b/src/Data/String.js @@ -131,6 +131,14 @@ exports.drop = function (n) { }; }; +exports.slice = function (b) { + return function (e) { + return function (s) { + return s.slice(b, e); + }; + }; +}; + exports.count = function (p) { return function (s) { var i = 0; diff --git a/src/Data/String.purs b/src/Data/String.purs index e9d5088..9ee45a5 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -26,6 +26,7 @@ module Data.String , drop , dropRight , dropWhile + , slice , stripPrefix , stripSuffix , count @@ -172,6 +173,17 @@ takeWhile p s = take (count p s) s dropWhile :: (Char -> Boolean) -> String -> String dropWhile p s = drop (count p s) s +-- | Returns the substring at indices [begin, end). +-- | If either index is negative, it is normalized to `length s - index`, +-- | where `s` is the input string. An empty string is returned if either +-- | index is out of bounds or if `begin > end`. +-- | +-- | ```purescript +-- | slice 0 1 "purescript" == "p" +-- | slice 3 6 "purescript" == "ecr" +-- | slice -4 -1 "purescript" == "rip" +foreign import slice :: Int -> Int -> String -> String + -- | If the string starts with the given prefix, return the portion of the -- | string left after removing it, as a Just value. Otherwise, return Nothing. -- | From 9a17b2f20eeb749fe803e1ac1ebb30650f77e5f0 Mon Sep 17 00:00:00 2001 From: Matthew Chan Date: Tue, 20 Mar 2018 17:30:00 -0700 Subject: [PATCH 128/186] fix comment --- src/Data/String.purs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Data/String.purs b/src/Data/String.purs index 9ee45a5..69f118d 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -174,9 +174,9 @@ dropWhile :: (Char -> Boolean) -> String -> String dropWhile p s = drop (count p s) s -- | Returns the substring at indices [begin, end). --- | If either index is negative, it is normalized to `length s - index`, +-- | If either index is negative, it is normalised to `length s - index`, -- | where `s` is the input string. An empty string is returned if either --- | index is out of bounds or if `begin > end`. +-- | index is out of bounds or if `begin > end` after normalisation. -- | -- | ```purescript -- | slice 0 1 "purescript" == "p" From f152cc5d1a4a2ed36a41a171425cf7dc9aea7fe3 Mon Sep 17 00:00:00 2001 From: Matthew Chan Date: Tue, 20 Mar 2018 17:31:11 -0700 Subject: [PATCH 129/186] close code block --- src/Data/String.purs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Data/String.purs b/src/Data/String.purs index 69f118d..718bec1 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -182,6 +182,7 @@ dropWhile p s = drop (count p s) s -- | slice 0 1 "purescript" == "p" -- | slice 3 6 "purescript" == "ecr" -- | slice -4 -1 "purescript" == "rip" +-- | ``` foreign import slice :: Int -> Int -> String -> String -- | If the string starts with the given prefix, return the portion of the From 9112112758f0d9fc3cd31ea0fa1041ea7bd1df4d Mon Sep 17 00:00:00 2001 From: Matthew Chan Date: Wed, 21 Mar 2018 12:13:47 -0700 Subject: [PATCH 130/186] wrap in maybe --- src/Data/String.js | 13 +++++++++---- src/Data/String.purs | 19 +++++++++++++------ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/Data/String.js b/src/Data/String.js index 76f8895..cb153a5 100644 --- a/src/Data/String.js +++ b/src/Data/String.js @@ -131,10 +131,15 @@ exports.drop = function (n) { }; }; -exports.slice = function (b) { - return function (e) { - return function (s) { - return s.slice(b, e); +exports._slice = function (just) { + return function (nothing) { + return function (b) { + return function (e) { + return function (s) { + var res = s.slice(b, e); + return (res.length == 0 ? nothing : just(res)); + }; + }; }; }; }; diff --git a/src/Data/String.purs b/src/Data/String.purs index 718bec1..b41f9ca 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -173,17 +173,24 @@ takeWhile p s = take (count p s) s dropWhile :: (Char -> Boolean) -> String -> String dropWhile p s = drop (count p s) s --- | Returns the substring at indices [begin, end). +-- | Returns the substring at indices `[begin, end)`. -- | If either index is negative, it is normalised to `length s - index`, --- | where `s` is the input string. An empty string is returned if either +-- | where `s` is the input string. `Nothing` is returned if either -- | index is out of bounds or if `begin > end` after normalisation. -- | -- | ```purescript --- | slice 0 1 "purescript" == "p" --- | slice 3 6 "purescript" == "ecr" --- | slice -4 -1 "purescript" == "rip" +-- | slice 0 1 "purescript" == Just "p" +-- | slice 3 6 "purescript" == Just "ecr" +-- | slice -4 -1 "purescript" == Just "rip" +-- | slice -4 3 "purescript" == Nothing -- | ``` -foreign import slice :: Int -> Int -> String -> String +slice :: Int -> Int -> String -> Maybe String +slice = _slice Just Nothing + +foreign import _slice + :: (forall a. a -> Maybe a) + -> (forall a. Maybe a) + -> Int -> Int -> String -> Maybe String -- | If the string starts with the given prefix, return the portion of the -- | string left after removing it, as a Just value. Otherwise, return Nothing. From 58492574c3e5678cacc69f8fcd19016da72c6c00 Mon Sep 17 00:00:00 2001 From: Matthew Chan Date: Wed, 21 Mar 2018 12:28:04 -0700 Subject: [PATCH 131/186] use === --- src/Data/String.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Data/String.js b/src/Data/String.js index cb153a5..c2c4d12 100644 --- a/src/Data/String.js +++ b/src/Data/String.js @@ -137,7 +137,7 @@ exports._slice = function (just) { return function (e) { return function (s) { var res = s.slice(b, e); - return (res.length == 0 ? nothing : just(res)); + return res.length === 0 ? nothing : just(res); }; }; }; From 05305ea62a4da60ce1cd72cafef42904f54df3a1 Mon Sep 17 00:00:00 2001 From: Matthew Chan Date: Fri, 23 Mar 2018 12:40:16 -0700 Subject: [PATCH 132/186] check bounds etc --- src/Data/String.js | 8 ++++++-- src/Data/String.purs | 7 ++++--- test/Test/Data/String.purs | 7 +++++++ 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/Data/String.js b/src/Data/String.js index c2c4d12..a10a98c 100644 --- a/src/Data/String.js +++ b/src/Data/String.js @@ -136,8 +136,12 @@ exports._slice = function (just) { return function (b) { return function (e) { return function (s) { - var res = s.slice(b, e); - return res.length === 0 ? nothing : just(res); + var b1 = b < 0 ? s.length + b : b; + var e1 = e < 0 ? s.length + e : e; + if ( b1 < 0 || e1 >= s.length || b1 > e1 ) + return nothing; + else + return just(s.slice(b,e)); }; }; }; diff --git a/src/Data/String.purs b/src/Data/String.purs index b41f9ca..594aeb2 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -179,10 +179,11 @@ dropWhile p s = drop (count p s) s -- | index is out of bounds or if `begin > end` after normalisation. -- | -- | ```purescript +-- | slice 0 0 "purescript" == Just "" -- | slice 0 1 "purescript" == Just "p" --- | slice 3 6 "purescript" == Just "ecr" --- | slice -4 -1 "purescript" == Just "rip" --- | slice -4 3 "purescript" == Nothing +-- | slice 3 6 "purescript" == Just "esc" +-- | slice (-4) (-1) "purescript" == Just "rip" +-- | slice (-4) 3 "purescript" == Nothing -- | ``` slice :: Int -> Int -> String -> Maybe String slice = _slice Just Nothing diff --git a/test/Test/Data/String.purs b/test/Test/Data/String.purs index fa89fa6..9768635 100644 --- a/test/Test/Data/String.purs +++ b/test/Test/Data/String.purs @@ -206,3 +206,10 @@ testString = do assert $ joinWith "" [] == "" assert $ joinWith "" ["a", "b"] == "ab" assert $ joinWith "--" ["a", "b", "c"] == "a--b--c" + + log "slice" + assert $ slice 0 0 "purescript" == Just "" + assert $ slice 0 1 "purescript" == Just "p" + assert $ slice 3 6 "purescript" == Just "esc" + assert $ slice (-4) (-1) "purescript" == Just "rip" + assert $ slice (-4) 3 "purescript" == Nothing From 31b21bbfcb823885a37312be9469f5728134cdff Mon Sep 17 00:00:00 2001 From: Matthew Chan Date: Fri, 23 Mar 2018 12:41:18 -0700 Subject: [PATCH 133/186] refine condition --- src/Data/String.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Data/String.js b/src/Data/String.js index a10a98c..aa1754a 100644 --- a/src/Data/String.js +++ b/src/Data/String.js @@ -138,7 +138,10 @@ exports._slice = function (just) { return function (s) { var b1 = b < 0 ? s.length + b : b; var e1 = e < 0 ? s.length + e : e; - if ( b1 < 0 || e1 >= s.length || b1 > e1 ) + if ( b1 < 0 || b1 >= s.length + || e1 < 0 || e1 >= s.length + || b1 > e1 + ) return nothing; else return just(s.slice(b,e)); From 1c518db1a70dbc2c43bc9764e6a8947cf41d91aa Mon Sep 17 00:00:00 2001 From: Matthew Chan Date: Fri, 23 Mar 2018 14:51:15 -0700 Subject: [PATCH 134/186] move bounds checking logic to purescript --- src/Data/String.js | 20 ++++---------------- src/Data/String.purs | 19 +++++++++++++------ 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/src/Data/String.js b/src/Data/String.js index aa1754a..e601fbf 100644 --- a/src/Data/String.js +++ b/src/Data/String.js @@ -131,22 +131,10 @@ exports.drop = function (n) { }; }; -exports._slice = function (just) { - return function (nothing) { - return function (b) { - return function (e) { - return function (s) { - var b1 = b < 0 ? s.length + b : b; - var e1 = e < 0 ? s.length + e : e; - if ( b1 < 0 || b1 >= s.length - || e1 < 0 || e1 >= s.length - || b1 > e1 - ) - return nothing; - else - return just(s.slice(b,e)); - }; - }; +exports._slice = function (b) { + return function (e) { + return function (s) { + return s.slice(b,e); }; }; }; diff --git a/src/Data/String.purs b/src/Data/String.purs index 594aeb2..b6625b5 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -186,12 +186,19 @@ dropWhile p s = drop (count p s) s -- | slice (-4) 3 "purescript" == Nothing -- | ``` slice :: Int -> Int -> String -> Maybe String -slice = _slice Just Nothing - -foreign import _slice - :: (forall a. a -> Maybe a) - -> (forall a. Maybe a) - -> Int -> Int -> String -> Maybe String +slice b e s = if b' < 0 || b' >= l || + e' < 0 || e' >= l || + b' > e' + then Nothing + else Just (_slice b e s) + where + l = length s + norm x | x < 0 = l + x + | otherwise = x + b' = norm b + e' = norm e + +foreign import _slice :: Int -> Int -> String -> String -- | If the string starts with the given prefix, return the portion of the -- | string left after removing it, as a Just value. Otherwise, return Nothing. From e2d4b1d920826bc2aaea3594324d548f063c4ad1 Mon Sep 17 00:00:00 2001 From: Matthew Chan Date: Mon, 26 Mar 2018 20:03:42 -0700 Subject: [PATCH 135/186] more tests --- test/Test/Data/String.purs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/Test/Data/String.purs b/test/Test/Data/String.purs index 9768635..eb2cf90 100644 --- a/test/Test/Data/String.purs +++ b/test/Test/Data/String.purs @@ -212,4 +212,8 @@ testString = do assert $ slice 0 1 "purescript" == Just "p" assert $ slice 3 6 "purescript" == Just "esc" assert $ slice (-4) (-1) "purescript" == Just "rip" - assert $ slice (-4) 3 "purescript" == Nothing + assert $ slice (-4) 3 "purescript" == Nothing -- b' > e' + assert $ slice 1000 3 "purescript" == Nothing -- b' > e' (subsumes b > l) + assert $ slice 2 (-15) "purescript" == Nothing -- e' < 0 + assert $ slice (-15) 9 "purescript" == Nothing -- b' < 0 + assert $ slice 3 1000 "purescript" == Nothing -- e > l From fd5a1394fad5d3da86263a1311158c0408f64494 Mon Sep 17 00:00:00 2001 From: Matthew Leon Date: Sun, 14 Jan 2018 21:31:01 -0500 Subject: [PATCH 136/186] add more String Gens corresponding to Char Gens --- src/Data/String/Gen.purs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Data/String/Gen.purs b/src/Data/String/Gen.purs index e981a11..08c902a 100644 --- a/src/Data/String/Gen.purs +++ b/src/Data/String/Gen.purs @@ -29,3 +29,16 @@ genAsciiString' = genString CG.genAsciiChar' -- | Generates a string made up of numeric digits. genDigitString :: forall m. MonadRec m => MonadGen m => m String genDigitString = genString CG.genDigitChar + +-- | Generates a string using characters from the basic Latin alphabet. +genAlphaString :: forall m. MonadRec m => MonadGen m => m String +genAlphaString = genString CG.genAlpha + +-- | Generates a string using lowercase characters from the basic Latin alphabet. +genAlphaLowercaseString :: forall m. MonadRec m => MonadGen m => m String +genAlphaLowercaseString = genString CG.genAlphaLowercase + +-- | Generates a string using uppercase characters from the basic Latin alphabet. +genAlphaUppercaseString :: forall m. MonadRec m => MonadGen m => m String +genAlphaUppercaseString = genString CG.genAlphaUppercase + From 58e66ed83bd65571b9fc0570b2c2da42c72b33b9 Mon Sep 17 00:00:00 2001 From: Matthew Leon Date: Sun, 11 Mar 2018 16:03:16 -0400 Subject: [PATCH 137/186] conversions between NonEmptyArray/NonEmptyString addresses https://github.com/purescript/purescript-strings/issues/101 This uses unsafePartial for the sake of efficiency. --- bower.json | 2 +- src/Data/String/NonEmpty.purs | 12 ++++++++++++ test/Test/Data/String/NonEmpty.purs | 8 ++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/bower.json b/bower.json index 385c62f..d9f3936 100644 --- a/bower.json +++ b/bower.json @@ -22,7 +22,7 @@ "purescript-maybe": "^3.0.0", "purescript-partial": "^1.2.0", "purescript-unfoldable": "^3.0.0", - "purescript-arrays": "^4.0.1", + "purescript-arrays": "^4.3.0", "purescript-integers": "^3.2.0" }, "devDependencies": { diff --git a/src/Data/String/NonEmpty.purs b/src/Data/String/NonEmpty.purs index b3d93a9..0fdd629 100644 --- a/src/Data/String/NonEmpty.purs +++ b/src/Data/String/NonEmpty.purs @@ -10,12 +10,14 @@ module Data.String.NonEmpty , fromString , unsafeFromString , fromCharArray + , fromNonEmptyCharArray , singleton , cons , snoc , fromFoldable1 , toString , toCharArray + , toNonEmptyCharArray , charAt , charCodeAt , toChar @@ -52,6 +54,8 @@ module Data.String.NonEmpty import Prelude +import Data.Array.NonEmpty (NonEmptyArray) +import Data.Array.NonEmpty as NEA import Data.Foldable (class Foldable) import Data.Foldable as F import Data.Maybe (Maybe(..), fromJust) @@ -60,6 +64,7 @@ import Data.Semigroup.Foldable as F1 import Data.String (Pattern(..)) import Data.String as String import Data.String.Unsafe as U +import Partial.Unsafe (unsafePartial) import Unsafe.Coerce (unsafeCoerce) -- | A string that is known not to be empty. @@ -110,6 +115,9 @@ fromCharArray = case _ of [] -> Nothing cs -> Just (NonEmptyString (String.fromCharArray cs)) +fromNonEmptyCharArray :: NonEmptyArray Char -> NonEmptyString +fromNonEmptyCharArray = unsafePartial fromJust <<< fromCharArray <<< NEA.toArray + -- | Creates a `NonEmptyString` from a character. singleton :: Char -> NonEmptyString singleton = NonEmptyString <<< String.singleton @@ -181,6 +189,10 @@ toChar (NonEmptyString s) = String.toChar s toCharArray :: NonEmptyString -> Array Char toCharArray (NonEmptyString s) = String.toCharArray s +-- | Converts the `NonEmptyString` into a non-empty array of characters. +toNonEmptyCharArray :: NonEmptyString -> NonEmptyArray Char +toNonEmptyCharArray = unsafePartial fromJust <<< NEA.fromArray <<< toCharArray + -- | Appends a string to this non-empty string. Since one of the strings is -- | non-empty we know the result will be too. -- | diff --git a/test/Test/Data/String/NonEmpty.purs b/test/Test/Data/String/NonEmpty.purs index fc80784..c473ff7 100644 --- a/test/Test/Data/String/NonEmpty.purs +++ b/test/Test/Data/String/NonEmpty.purs @@ -4,6 +4,7 @@ import Data.String.NonEmpty import Control.Monad.Eff (Eff) import Control.Monad.Eff.Console (CONSOLE, log) +import Data.Array.NonEmpty as NEA import Data.Array.Partial as AP import Data.Foldable (class Foldable, foldl) import Data.Maybe (Maybe(..), fromJust, isNothing, maybe) @@ -22,6 +23,9 @@ testNonEmptyString = do assert $ fromCharArray [] == Nothing assert $ fromCharArray ['a', 'b'] == Just (nes "ab") + log "fromNonEmptyCharArray" + assert $ fromNonEmptyCharArray (NEA.singleton 'b') == singleton 'b' + log "singleton" assert $ singleton 'a' == nes "a" @@ -64,6 +68,10 @@ testNonEmptyString = do assert $ toCharArray (nes "ab") == ['a', 'b'] assert $ toCharArray (nes "Hello☺\n") == ['H','e','l','l','o','☺','\n'] + log "toNonEmptyCharArray" + assert $ toNonEmptyCharArray (nes "ab") + == unsafePartial fromJust (NEA.fromArray ['a', 'b']) + log "appendString" assert $ appendString (nes "Hello") " world" == nes "Hello world" assert $ appendString (nes "Hello") "" == nes "Hello" From 1f8411dfe6b2e54166ddba9c5179e0ce16efe18d Mon Sep 17 00:00:00 2001 From: Matthew Leon Date: Sun, 1 Apr 2018 14:57:25 -0400 Subject: [PATCH 138/186] add benchmarks --- bench/Main.purs | 57 +++++++++++++++++++++++++++++++++++++++++++++++++ bower.json | 3 ++- package.json | 6 +++++- 3 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 bench/Main.purs diff --git a/bench/Main.purs b/bench/Main.purs new file mode 100644 index 0000000..87c1ca0 --- /dev/null +++ b/bench/Main.purs @@ -0,0 +1,57 @@ +module Bench.Main where + +import Prelude + +import Control.Monad.Eff (Eff) +import Control.Monad.Eff.Console (CONSOLE, log) +import Data.Array.NonEmpty (fromArray) +import Data.Maybe (fromJust) +import Data.String (toCharArray) +import Data.String.NonEmpty (fromFoldable1, fromNonEmptyCharArray) +import Partial.Unsafe (unsafePartial) +import Performance.Minibench (benchWith) + +main :: Eff (console :: CONSOLE) Unit +main = do + log "NonEmpty conversions" + log "======" + log "" + benchNonEmptyConversions + +benchNonEmptyConversions :: Eff (console :: CONSOLE) Unit +benchNonEmptyConversions = do + log "fromNonEmptyCharArray: short" + log "---" + benchFromNonEmptyCharArray + log "" + + log "fromFoldable1" + log "---" + benchFromFoldable1 + log "" + + where + + benchFromNonEmptyCharArray = do + log "short string" + bench \_ -> fromNonEmptyCharArray shortStringArr + + log "long string" + bench \_ -> fromNonEmptyCharArray longStringArr + + benchFromFoldable1 = do + log "short string" + bench \_ -> fromFoldable1 shortStringArr + + log "long string" + bench \_ -> fromFoldable1 longStringArr + + shortStringArr = unsafePartial fromJust $ fromArray + $ toCharArray "supercalifragilisticexpialidocious" + longStringArr = unsafePartial fromJust $ fromArray + $ toCharArray loremIpsum + + bench = benchWith 100000 + +loremIpsum :: String +loremIpsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut aliquet euismod ligula, vitae lacinia lorem imperdiet nec. Nulla volutpat ullamcorper mollis. Proin interdum quam a sem auctor, id tempus nisl pretium. Suspendisse potenti. Quisque ut libero consequat, suscipit sem a, malesuada nisi. Aliquam dictum odio mi, eu laoreet felis scelerisque non. Ut in odio vehicula, cursus augue sed, tincidunt lorem. Vestibulum consequat lectus eu commodo vulputate. Nam vitae faucibus ipsum. Curabitur sit amet neque sed est sagittis vehicula nec nec risus. Phasellus consectetur cursus malesuada. Vestibulum commodo lorem ut mauris mollis faucibus. Integer ut massa auctor, scelerisque nisi nec, rutrum nisl. Integer vel ex sem. Sed purus felis, molestie eget cursus vel, maximus ut augue. Curabitur nunc ligula, lobortis vitae vehicula a, volutpat nec sem. Phasellus non sapien ipsum. Mauris dolor justo, mollis at elit a, sollicitudin commodo quam. Curabitur posuere felis at nunc pharetra, eu convallis lectus dapibus. Aliquam ullamcorper porta fermentum. Donec at tellus metus. Donec pharetra tempor odio sit amet viverra. Nam vel metus libero. Vivamus maximus quis lacus id pharetra. Duis sed diam molestie, sodales leo id, pulvinar justo. In non augue tempor risus consectetur hendrerit. In libero nulla, elementum non ultrices eu, vehicula non ipsum. Maecenas in hendrerit tellus, sodales dignissim turpis. Ut odio diam, convallis in elit non, consequat gravida nisi. Cras egestas metus eleifend sapien efficitur, vel vulputate est porta. Aliquam posuere, magna nec bibendum luctus, quam risus efficitur sapien, id volutpat metus ex non lorem. Praesent velit eros, efficitur sed tortor quis, lobortis eleifend ligula. Sed tellus quam, aliquet vitae sagittis a, egestas eget massa. Etiam odio elit, hendrerit vel dui vel, fermentum pharetra neque. Curabitur quis mauris id lacus consectetur rhoncus non nec mauris. Mauris blandit tempor pretium. Donec non nisi finibus, lobortis dolor vitae, euismod arcu. Nullam scelerisque lacus in dolor volutpat mollis. Nunc vitae consectetur ligula, quis laoreet quam.Proin sit amet nisi eu orci hendrerit imperdiet vitae sit amet leo. Donec sodales id ante eget viverra. Nullam vitae elit in mauris accumsan feugiat id a velit. Nulla facilisi. Cras in turpis efficitur, consectetur justo quis, suscipit tortor. Sed tincidunt pellentesque sapien, in ultricies eros rhoncus sit amet. Integer blandit ornare lobortis. Duis dictum sit amet mauris sit amet cursus. Nullam nec nisl mauris. Praesent cursus imperdiet mi mattis luctus. Donec in tortor fermentum, efficitur turpis vel, facilisis augue. Integer egestas nisl et magna volutpat ornare. Donec pulvinar risus elit, eget viverra est feugiat in.Ut nec ante vestibulum neque pulvinar pretium sit amet eu nisi. Aliquam erat volutpat. Maecenas egestas nisi et mi congue, sed ultricies nibh posuere. Suspendisse potenti. Donec a nulla et velit elementum pretium. Pellentesque gravida imperdiet sem et varius. Praesent ac diam diam. Donec iaculis risus ex, ac eleifend sapien luctus ut. Fusce aliquet, lacus tincidunt porta malesuada, massa augue commodo nulla, ac malesuada tortor est sed eros. Praesent mattis, nisi eget ullamcorper vestibulum, lacus ante placerat metus, ac ullamcorper ante tellus vel nulla. Praesent vehicula in est sit amet varius. Sed facilisis felis sed sem porttitor rutrum. Etiam sollicitudin erat neque, id gravida metus scelerisque quis. Proin venenatis pharetra lectus ac auctor." diff --git a/bower.json b/bower.json index d9f3936..a20beba 100644 --- a/bower.json +++ b/bower.json @@ -27,6 +27,7 @@ }, "devDependencies": { "purescript-assert": "^3.0.0", - "purescript-console": "^3.0.0" + "purescript-console": "^3.0.0", + "purescript-minibench": "^1.0.1" } } diff --git a/package.json b/package.json index 132cefc..0f1ba83 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,11 @@ "scripts": { "clean": "rimraf output && rimraf .pulp-cache", "build": "eslint src && pulp build -- --censor-lib --strict", - "test": "pulp test" + "test": "pulp test", + + "bench:build": "purs compile 'bench/**/*.purs' 'src/**/*.purs' 'bower_components/*/src/**/*.purs'", + "bench:run": "node --expose-gc -e 'require(\"./output/Bench.Main/index.js\").main()'", + "bench": "npm run bench:build && npm run bench:run" }, "devDependencies": { "eslint": "^3.17.1", From 00030c2c1f2168f662659f06de559ee0f3648fff Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Tue, 17 Apr 2018 01:25:53 +0100 Subject: [PATCH 139/186] Updates for 0.12 --- bower.json | 18 +++++++++--------- src/Data/String/Regex/Flags.purs | 2 -- test/Test/Data/Char.purs | 8 ++++---- test/Test/Data/String.purs | 8 ++++---- test/Test/Data/String/CaseInsensitive.purs | 8 ++++---- test/Test/Data/String/CodePoints.purs | 8 ++++---- test/Test/Data/String/NonEmpty.purs | 8 ++++---- test/Test/Data/String/Regex.purs | 8 ++++---- test/Test/Data/String/Unsafe.purs | 8 ++++---- test/Test/Main.purs | 7 +++---- 10 files changed, 40 insertions(+), 43 deletions(-) diff --git a/bower.json b/bower.json index 385c62f..1fe3ac2 100644 --- a/bower.json +++ b/bower.json @@ -17,16 +17,16 @@ "package.json" ], "dependencies": { - "purescript-either": "^3.0.0", - "purescript-gen": "^1.1.0", - "purescript-maybe": "^3.0.0", - "purescript-partial": "^1.2.0", - "purescript-unfoldable": "^3.0.0", - "purescript-arrays": "^4.0.1", - "purescript-integers": "^3.2.0" + "purescript-either": "#compiler/0.12", + "purescript-gen": "#compiler/0.12", + "purescript-maybe": "#compiler/0.12", + "purescript-partial": "#compiler/0.12", + "purescript-unfoldable": "#compiler/0.12", + "purescript-arrays": "#compiler/0.12", + "purescript-integers": "#compiler/0.12" }, "devDependencies": { - "purescript-assert": "^3.0.0", - "purescript-console": "^3.0.0" + "purescript-assert": "#compiler/0.12", + "purescript-console": "#compiler/0.12" } } diff --git a/src/Data/String/Regex/Flags.purs b/src/Data/String/Regex/Flags.purs index 2684498..bd14d8c 100644 --- a/src/Data/String/Regex/Flags.purs +++ b/src/Data/String/Regex/Flags.purs @@ -3,8 +3,6 @@ module Data.String.Regex.Flags where import Prelude import Control.MonadPlus (guard) - -import Data.Monoid (class Monoid) import Data.String (joinWith) type RegexFlagsRec = diff --git a/test/Test/Data/Char.purs b/test/Test/Data/Char.purs index ee86573..869a2ca 100644 --- a/test/Test/Data/Char.purs +++ b/test/Test/Data/Char.purs @@ -2,14 +2,14 @@ module Test.Data.Char (testChar) where import Prelude (Unit, (==), ($), discard) -import Control.Monad.Eff (Eff) -import Control.Monad.Eff.Console (CONSOLE, log) +import Effect (Effect) +import Effect.Console (log) import Data.Char -import Test.Assert (ASSERT, assert) +import Test.Assert (assert) -testChar :: forall eff. Eff (console :: CONSOLE, assert :: ASSERT | eff) Unit +testChar :: Effect Unit testChar = do log "toCharCode" assert $ toCharCode 'a' == 97 diff --git a/test/Test/Data/String.purs b/test/Test/Data/String.purs index eb2cf90..24f0a4f 100644 --- a/test/Test/Data/String.purs +++ b/test/Test/Data/String.purs @@ -2,15 +2,15 @@ module Test.Data.String (testString) where import Prelude (Unit, Ordering(..), (==), ($), discard, negate, not, (/=), (&&)) -import Control.Monad.Eff (Eff) -import Control.Monad.Eff.Console (CONSOLE, log) +import Effect (Effect) +import Effect.Console (log) import Data.Maybe (Maybe(..), isNothing, maybe) import Data.String -import Test.Assert (ASSERT, assert) +import Test.Assert (assert) -testString :: forall eff. Eff (console :: CONSOLE, assert :: ASSERT | eff) Unit +testString :: Effect Unit testString = do log "charAt" assert $ charAt 0 "" == Nothing diff --git a/test/Test/Data/String/CaseInsensitive.purs b/test/Test/Data/String/CaseInsensitive.purs index 9bd4530..ec7d4bf 100644 --- a/test/Test/Data/String/CaseInsensitive.purs +++ b/test/Test/Data/String/CaseInsensitive.purs @@ -2,14 +2,14 @@ module Test.Data.String.CaseInsensitive (testCaseInsensitiveString) where import Prelude (Unit, (==), ($), discard, compare, Ordering(..)) -import Control.Monad.Eff (Eff) -import Control.Monad.Eff.Console (CONSOLE, log) +import Effect (Effect) +import Effect.Console (log) import Data.String.CaseInsensitive -import Test.Assert (ASSERT, assert) +import Test.Assert (assert) -testCaseInsensitiveString :: forall eff. Eff (console :: CONSOLE, assert :: ASSERT | eff) Unit +testCaseInsensitiveString :: Effect Unit testCaseInsensitiveString = do log "equality" assert $ CaseInsensitiveString "aB" == CaseInsensitiveString "AB" diff --git a/test/Test/Data/String/CodePoints.purs b/test/Test/Data/String/CodePoints.purs index 75c3573..56ba501 100644 --- a/test/Test/Data/String/CodePoints.purs +++ b/test/Test/Data/String/CodePoints.purs @@ -2,19 +2,19 @@ module Test.Data.String.CodePoints (testStringCodePoints) where import Prelude -import Control.Monad.Eff (Eff) -import Control.Monad.Eff.Console (CONSOLE, log) +import Effect (Effect) +import Effect.Console (log) import Data.Char (fromCharCode) import Data.Maybe (Maybe(..), isNothing, maybe) import Data.String.CodePoints -import Test.Assert (ASSERT, assert) +import Test.Assert (assert) str :: String str = "a\xDC00\xD800\xD800\x16805\x16A06\&z" -testStringCodePoints :: forall eff. Eff (console :: CONSOLE, assert :: ASSERT | eff) Unit +testStringCodePoints :: Effect Unit testStringCodePoints = do log "show" assert $ map show (codePointAt 0 str) == Just "(CodePoint 0x61)" diff --git a/test/Test/Data/String/NonEmpty.purs b/test/Test/Data/String/NonEmpty.purs index fc80784..e9cd771 100644 --- a/test/Test/Data/String/NonEmpty.purs +++ b/test/Test/Data/String/NonEmpty.purs @@ -2,17 +2,17 @@ module Test.Data.String.NonEmpty (testNonEmptyString) where import Data.String.NonEmpty -import Control.Monad.Eff (Eff) -import Control.Monad.Eff.Console (CONSOLE, log) +import Effect (Effect) +import Effect.Console (log) import Data.Array.Partial as AP import Data.Foldable (class Foldable, foldl) import Data.Maybe (Maybe(..), fromJust, isNothing, maybe) import Data.Semigroup.Foldable (class Foldable1, foldMap1Default) import Partial.Unsafe (unsafePartial) import Prelude (class Functor, Ordering(..), Unit, append, discard, negate, not, ($), (&&), (/=), (==)) -import Test.Assert (ASSERT, assert) +import Test.Assert (assert) -testNonEmptyString :: forall eff. Eff (console :: CONSOLE, assert :: ASSERT | eff) Unit +testNonEmptyString :: Effect Unit testNonEmptyString = do log "fromString" assert $ fromString "" == Nothing diff --git a/test/Test/Data/String/Regex.purs b/test/Test/Data/String/Regex.purs index 6228b91..0faaae2 100644 --- a/test/Test/Data/String/Regex.purs +++ b/test/Test/Data/String/Regex.purs @@ -2,8 +2,8 @@ module Test.Data.String.Regex (testStringRegex) where import Prelude (Unit, ($), (<>), discard, (==), not) -import Control.Monad.Eff (Eff) -import Control.Monad.Eff.Console (CONSOLE, log) +import Effect (Effect) +import Effect.Console (log) import Data.Either (isLeft) import Data.Maybe (Maybe(..)) @@ -11,9 +11,9 @@ import Data.String.Regex import Data.String.Regex.Flags (global, ignoreCase, noFlags) import Data.String.Regex.Unsafe (unsafeRegex) -import Test.Assert (ASSERT, assert) +import Test.Assert (assert) -testStringRegex :: forall eff. Eff (console :: CONSOLE, assert :: ASSERT | eff) Unit +testStringRegex :: Effect Unit testStringRegex = do log "regex" assert $ test (unsafeRegex "^a" noFlags) "abc" diff --git a/test/Test/Data/String/Unsafe.purs b/test/Test/Data/String/Unsafe.purs index bd2924d..c87055a 100644 --- a/test/Test/Data/String/Unsafe.purs +++ b/test/Test/Data/String/Unsafe.purs @@ -2,14 +2,14 @@ module Test.Data.String.Unsafe (testStringUnsafe) where import Prelude (Unit, (==), ($), discard) -import Control.Monad.Eff (Eff) -import Control.Monad.Eff.Console (CONSOLE, log) +import Effect (Effect) +import Effect.Console (log) import Data.String.Unsafe -import Test.Assert (ASSERT, assert) +import Test.Assert (assert) -testStringUnsafe :: forall eff. Eff (console :: CONSOLE, assert :: ASSERT | eff) Unit +testStringUnsafe :: Effect Unit testStringUnsafe = do log "charCodeAt" assert $ charCodeAt 0 "ab" == 97 diff --git a/test/Test/Main.purs b/test/Test/Main.purs index e81600f..92d6cd1 100644 --- a/test/Test/Main.purs +++ b/test/Test/Main.purs @@ -2,9 +2,8 @@ module Test.Main where import Prelude -import Control.Monad.Eff (Eff) -import Control.Monad.Eff.Console (CONSOLE, log) -import Test.Assert (ASSERT) +import Effect (Effect) +import Effect.Console (log) import Test.Data.Char (testChar) import Test.Data.String (testString) import Test.Data.String.CaseInsensitive (testCaseInsensitiveString) @@ -13,7 +12,7 @@ import Test.Data.String.NonEmpty (testNonEmptyString) import Test.Data.String.Regex (testStringRegex) import Test.Data.String.Unsafe (testStringUnsafe) -main :: Eff (console :: CONSOLE, assert :: ASSERT) Unit +main :: Effect Unit main = do log "\n--- Data.Char ---\n" testChar From d6c6b58e5b733899233f6dc9e8de1c83eafc719b Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Fri, 18 May 2018 15:39:11 +0100 Subject: [PATCH 140/186] 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 From bc164b95a6d07ae6159230fcf448b9f007f5bb92 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Fri, 18 May 2018 16:01:12 +0100 Subject: [PATCH 141/186] Rename count to countPrefix #81 --- src/Data/String.js | 2 +- src/Data/String.purs | 10 +++++----- src/Data/String/CodePoints.js | 2 +- src/Data/String/CodePoints.purs | 14 +++++++------- src/Data/String/NonEmpty.purs | 8 ++++---- test/Test/Data/String.purs | 10 +++++----- test/Test/Data/String/CodePoints.purs | 12 ++++++------ test/Test/Data/String/NonEmpty.purs | 10 +++++----- 8 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/Data/String.js b/src/Data/String.js index 1c46b9a..3079ddc 100644 --- a/src/Data/String.js +++ b/src/Data/String.js @@ -139,7 +139,7 @@ exports._slice = function (b) { }; }; -exports.count = function (p) { +exports.countPrefix = function (p) { return function (s) { var i = 0; while (i < s.length && p(s.charAt(i))) i++; diff --git a/src/Data/String.purs b/src/Data/String.purs index c377316..4c6843e 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -29,7 +29,7 @@ module Data.String , slice , stripPrefix , stripSuffix - , count + , countPrefix , split , splitAt , toCharArray @@ -162,7 +162,7 @@ uncons s = Just { head: U.charAt zero s, tail: drop one s } -- | ``` -- | takeWhile :: (Char -> Boolean) -> String -> String -takeWhile p s = take (count p s) s +takeWhile p s = take (countPrefix p s) s -- | Returns the suffix remaining after `takeWhile`. -- | @@ -171,7 +171,7 @@ takeWhile p s = take (count p s) s -- | ``` -- | dropWhile :: (Char -> Boolean) -> String -> String -dropWhile p s = drop (count p s) s +dropWhile p s = drop (countPrefix p s) s -- | Returns the substring at indices `[begin, end)`. -- | If either index is negative, it is normalised to `length s - index`, @@ -407,10 +407,10 @@ dropRight i s = take (length s - i) s -- | of the string for which the predicate holds. -- | -- | ```purescript --- | count (_ /= ' ') "Hello World" == 5 -- since length "Hello" == 5 +-- | countPrefix (_ /= ' ') "Hello World" == 5 -- since length "Hello" == 5 -- | ``` -- | -foreign import count :: (Char -> Boolean) -> String -> Int +foreign import countPrefix :: (Char -> Boolean) -> String -> Int -- | Returns the substrings of the second string separated along occurences -- | of the first string. diff --git a/src/Data/String/CodePoints.js b/src/Data/String/CodePoints.js index 1c73483..eead7f6 100644 --- a/src/Data/String/CodePoints.js +++ b/src/Data/String/CodePoints.js @@ -40,7 +40,7 @@ exports._codePointAt = function (fallback) { }; }; -exports._count = function (fallback) { +exports._countPrefix = function (fallback) { return function (unsafeCodePointAt0) { if (hasStringIterator) { return function (pred) { diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index a48a764..d51d180 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -9,7 +9,7 @@ module Data.String.CodePoints , codePointFromInt , codePointToInt , codePointFromChar - , count + , countPrefix , drop , dropWhile , fromCodePointArray @@ -168,14 +168,14 @@ codePointAtFallback n s = case uncons s of -- | time linear to the length of the string. -- | -- | ```purescript --- | >>> count (\c -> codePointToInt c == 0x1D400) "𝐀𝐀 b c 𝐀" +-- | >>> countPrefix (\c -> codePointToInt c == 0x1D400) "𝐀𝐀 b c 𝐀" -- | 2 -- | ``` -- | -count :: (CodePoint -> Boolean) -> String -> Int -count = _count countFallback unsafeCodePointAt0 +countPrefix :: (CodePoint -> Boolean) -> String -> Int +countPrefix = _countPrefix countFallback unsafeCodePointAt0 -foreign import _count +foreign import _countPrefix :: ((CodePoint -> Boolean) -> String -> Int) -> (String -> CodePoint) -> (CodePoint -> Boolean) @@ -217,7 +217,7 @@ drop n s = String.drop (String.length (take n s)) s -- | ``` -- | dropWhile :: (CodePoint -> Boolean) -> String -> String -dropWhile p s = drop (count p s) s +dropWhile p s = drop (countPrefix p s) s -- | Creates a string from an array of code points. Operates in space and time @@ -402,7 +402,7 @@ takeFallback n s = case uncons s of -- | ``` -- | takeWhile :: (CodePoint -> Boolean) -> String -> String -takeWhile p s = take (count p s) s +takeWhile p s = take (countPrefix p s) s -- | Creates an array of code points from a string. Operates in space and time diff --git a/src/Data/String/NonEmpty.purs b/src/Data/String/NonEmpty.purs index 99396df..a6f50e2 100644 --- a/src/Data/String/NonEmpty.purs +++ b/src/Data/String/NonEmpty.purs @@ -41,7 +41,7 @@ module Data.String.NonEmpty , dropWhile , stripPrefix , stripSuffix - , count + , countPrefix , splitAt , toLower , toUpper @@ -409,10 +409,10 @@ dropRight i (NonEmptyString s) -- | for which the predicate holds. -- | -- | ```purescript --- | count (_ /= 'o') (NonEmptyString "Hello World") == 4 +-- | countPrefix (_ /= 'o') (NonEmptyString "Hello World") == 4 -- | ``` -count :: (Char -> Boolean) -> NonEmptyString -> Int -count = liftS <<< String.count +countPrefix :: (Char -> Boolean) -> NonEmptyString -> Int +countPrefix = liftS <<< String.countPrefix -- | Returns the substrings of a split at the given index, if the index is -- | within bounds. diff --git a/test/Test/Data/String.purs b/test/Test/Data/String.purs index a99fbbe..855ee5c 100644 --- a/test/Test/Data/String.purs +++ b/test/Test/Data/String.purs @@ -160,11 +160,11 @@ testString = do assert $ dropRight 3 "ab" == "" assert $ dropRight (-1) "ab" == "ab" - log "count" - assert $ count (_ == 'a') "" == 0 - assert $ count (_ == 'a') "ab" == 1 - assert $ count (_ == 'a') "aaab" == 3 - assert $ count (_ == 'a') "abaa" == 1 + log "countPrefix" + assert $ countPrefix (_ == 'a') "" == 0 + assert $ countPrefix (_ == 'a') "ab" == 1 + assert $ countPrefix (_ == 'a') "aaab" == 3 + assert $ countPrefix (_ == 'a') "abaa" == 1 log "split" assert $ split (Pattern "") "" == [] diff --git a/test/Test/Data/String/CodePoints.purs b/test/Test/Data/String/CodePoints.purs index 7d7a8cc..29c282d 100644 --- a/test/Test/Data/String/CodePoints.purs +++ b/test/Test/Data/String/CodePoints.purs @@ -41,12 +41,12 @@ testStringCodePoints = do assert $ Just (codePointFromChar $ fromCharCode 0) == codePointFromInt 0 assert $ Just (codePointFromChar $ fromCharCode 0xFFFF) == codePointFromInt 0xFFFF - log "count" - assert $ count (\_ -> true) "" == 0 - assert $ count (\_ -> false) str == 0 - assert $ count (\_ -> true) str == 7 - assert $ count (\x -> codePointToInt x < 0xFFFF) str == 4 - assert $ count (\x -> codePointToInt x < 0xDC00) str == 1 + log "countPrefix" + assert $ countPrefix (\_ -> true) "" == 0 + assert $ countPrefix (\_ -> false) str == 0 + assert $ countPrefix (\_ -> true) str == 7 + assert $ countPrefix (\x -> codePointToInt x < 0xFFFF) str == 4 + assert $ countPrefix (\x -> codePointToInt x < 0xDC00) str == 1 log "drop" assert $ drop (-1) str == str diff --git a/test/Test/Data/String/NonEmpty.purs b/test/Test/Data/String/NonEmpty.purs index 89b389f..e1f0983 100644 --- a/test/Test/Data/String/NonEmpty.purs +++ b/test/Test/Data/String/NonEmpty.purs @@ -201,11 +201,11 @@ testNonEmptyString = do assert $ dropRight 3 (nes "ab") == Nothing assert $ dropRight (-1) (nes "ab") == Just (nes "ab") - log "count" - assert $ count (_ == 'a') (nes "ab") == 1 - assert $ count (_ == 'a') (nes "aaab") == 3 - assert $ count (_ == 'a') (nes "abaa") == 1 - assert $ count (_ == 'c') (nes "abaa") == 0 + log "countPrefix" + assert $ countPrefix (_ == 'a') (nes "ab") == 1 + assert $ countPrefix (_ == 'a') (nes "aaab") == 3 + assert $ countPrefix (_ == 'a') (nes "abaa") == 1 + assert $ countPrefix (_ == 'c') (nes "abaa") == 0 log "splitAt" let From e019df0d24f5d93b64dd9a27e8fa8751d04801e4 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Sat, 19 May 2018 01:28:48 +0100 Subject: [PATCH 142/186] WIP --- src/Data/String.js | 128 -------- src/Data/String.purs | 361 +---------------------- src/Data/String/CodePoint.purs | 446 ++++++++++++++++++++++++++++ src/Data/String/CodePoints.purs | 19 +- src/Data/String/CodeUnits.js | 119 ++++++++ src/Data/String/CodeUnits.purs | 294 +++++++++++++++++++ src/Data/String/Gen.purs | 5 +- src/Data/String/NonEmpty.purs | 499 -------------------------------- src/Data/String/Pattern.purs | 33 +++ 9 files changed, 907 insertions(+), 997 deletions(-) create mode 100644 src/Data/String/CodePoint.purs create mode 100644 src/Data/String/CodeUnits.js create mode 100644 src/Data/String/CodeUnits.purs delete mode 100644 src/Data/String/NonEmpty.purs create mode 100644 src/Data/String/Pattern.purs diff --git a/src/Data/String.js b/src/Data/String.js index 3079ddc..3237741 100644 --- a/src/Data/String.js +++ b/src/Data/String.js @@ -1,95 +1,5 @@ "use strict"; -exports._charAt = function (just) { - return function (nothing) { - return function (i) { - return function (s) { - return i >= 0 && i < s.length ? just(s.charAt(i)) : nothing; - }; - }; - }; -}; - -exports.singleton = function (c) { - return c; -}; - -exports._charCodeAt = function (just) { - return function (nothing) { - return function (i) { - return function (s) { - return i >= 0 && i < s.length ? just(s.charCodeAt(i)) : nothing; - }; - }; - }; -}; - -exports._toChar = function (just) { - return function (nothing) { - return function (s) { - return s.length === 1 ? just(s) : nothing; - }; - }; -}; - -exports.fromCharArray = function (a) { - return a.join(""); -}; - -exports._indexOf = function (just) { - return function (nothing) { - return function (x) { - return function (s) { - var i = s.indexOf(x); - return i === -1 ? nothing : just(i); - }; - }; - }; -}; - -exports["_indexOf'"] = function (just) { - return function (nothing) { - 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); - }; - }; - }; - }; -}; - -exports._lastIndexOf = function (just) { - return function (nothing) { - return function (x) { - return function (s) { - var i = s.lastIndexOf(x); - return i === -1 ? nothing : just(i); - }; - }; - }; -}; - -exports["_lastIndexOf'"] = function (just) { - return function (nothing) { - 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); - }; - }; - }; - }; -}; - -exports.length = function (s) { - return s.length; -}; - exports._localeCompare = function (lt) { return function (eq) { return function (gt) { @@ -119,50 +29,12 @@ exports.replaceAll = function (s1) { }; }; -exports.take = function (n) { - return function (s) { - return s.substr(0, n); - }; -}; - -exports.drop = function (n) { - return function (s) { - return s.substring(n); - }; -}; - -exports._slice = function (b) { - return function (e) { - return function (s) { - return s.slice(b,e); - }; - }; -}; - -exports.countPrefix = function (p) { - return function (s) { - var i = 0; - while (i < s.length && p(s.charAt(i))) i++; - return i; - }; -}; - exports.split = function (sep) { return function (s) { return s.split(sep); }; }; -exports.splitAt = function (i) { - return function (s) { - return { before: s.substring(0, i), after: s.substring(i) }; - }; -}; - -exports.toCharArray = function (s) { - return s.split(""); -}; - exports.toLower = function (s) { return s.toLowerCase(); }; diff --git a/src/Data/String.purs b/src/Data/String.purs index 4c6843e..b6fbd0d 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -2,37 +2,15 @@ -- | A String represents a sequence of characters. -- | For details of the underlying implementation, see [String Reference at MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String). module Data.String - ( Pattern(..) - , Replacement(..) - , charAt - , charCodeAt - , fromCharArray - , toChar + ( module Data.String.Pattern , contains - , indexOf - , indexOf' - , lastIndexOf - , lastIndexOf' , null - , uncons - , length - , singleton , localeCompare , replace , replaceAll - , take - , takeRight - , takeWhile - , drop - , dropRight - , dropWhile - , slice , stripPrefix , stripSuffix - , countPrefix , split - , splitAt - , toCharArray , toLower , toUpper , trim @@ -42,95 +20,8 @@ module Data.String import Prelude import Data.Maybe (Maybe(..), isJust) -import Data.Newtype (class Newtype) -import Data.String.Unsafe as U - --- | A newtype used in cases where there is a string to be matched. --- | --- | ```purescript --- | pursPattern = Pattern ".purs" --- | --can be used like this: --- | contains pursPattern "Test.purs" --- | == true --- | ``` --- | -newtype Pattern = Pattern String - -derive instance eqPattern :: Eq Pattern -derive instance ordPattern :: Ord Pattern -derive instance newtypePattern :: Newtype Pattern _ - -instance showPattern :: Show Pattern where - show (Pattern s) = "(Pattern " <> show s <> ")" - --- | A newtype used in cases to specify a replacement for a pattern. -newtype Replacement = Replacement String - -derive instance eqReplacement :: Eq Replacement -derive instance ordReplacement :: Ord Replacement -derive instance newtypeReplacement :: Newtype Replacement _ - -instance showReplacement :: Show Replacement where - show (Replacement s) = "(Replacement " <> show s <> ")" - --- | Returns the character at the given index, if the index is within bounds. --- | --- | ```purescript --- | charAt 2 "Hello" == Just 'l' --- | charAt 10 "Hello" == Nothing --- | ``` --- | -charAt :: Int -> String -> Maybe Char -charAt = _charAt Just Nothing - -foreign import _charAt - :: (forall a. a -> Maybe a) - -> (forall a. Maybe a) - -> Int - -> String - -> Maybe Char - --- | Returns a string of length `1` containing the given character. --- | --- | ```purescript --- | singleton 'l' == "l" --- | ``` --- | -foreign import singleton :: Char -> String - --- | Returns the numeric Unicode value of the character at the given index, --- | if the index is within bounds. --- | ```purescript --- | charCodeAt 2 "5 €" == Just 0x20AC --- | charCodeAt 10 "5 €" == Nothing --- | ``` --- | -charCodeAt :: Int -> String -> Maybe Int -charCodeAt = _charCodeAt Just Nothing - -foreign import _charCodeAt - :: (forall a. a -> Maybe a) - -> (forall a. Maybe a) - -> Int - -> String - -> Maybe Int - --- | Converts the string to a character, if the length of the string is --- | exactly `1`. --- | --- | ```purescript --- | toChar "l" == Just 'l' --- | toChar "Hi" == Nothing -- since length is not 1 --- | ``` --- | -toChar :: String -> Maybe Char -toChar = _toChar Just Nothing - -foreign import _toChar - :: (forall a. a -> Maybe a) - -> (forall a. Maybe a) - -> String - -> Maybe Char +import Data.String.CodeUnits as SCU +import Data.String.Pattern (Pattern(..), Replacement(..)) -- | Returns `true` if the given string is empty. -- | @@ -138,68 +29,9 @@ foreign import _toChar -- | null "" == true -- | null "Hi" == false -- | ``` --- | null :: String -> Boolean null s = s == "" --- | Returns the first character and the rest of the string, --- | if the string is not empty. --- | --- | ```purescript --- | uncons "" == Nothing --- | uncons "Hello World" == Just { head: 'H', tail: "ello World" } --- | ``` --- | -uncons :: String -> Maybe { head :: Char, tail :: String } -uncons "" = Nothing -uncons s = Just { head: U.charAt zero s, tail: drop one s } - --- | Returns the longest prefix (possibly empty) of characters that satisfy --- | the predicate. --- | --- | ```purescript --- | takeWhile (_ /= ':') "http://purescript.org" == "http" --- | ``` --- | -takeWhile :: (Char -> Boolean) -> String -> String -takeWhile p s = take (countPrefix p s) s - --- | Returns the suffix remaining after `takeWhile`. --- | --- | ```purescript --- | dropWhile (_ /= '.') "Test.purs" == ".purs" --- | ``` --- | -dropWhile :: (Char -> Boolean) -> String -> String -dropWhile p s = drop (countPrefix p s) s - --- | Returns the substring at indices `[begin, end)`. --- | If either index is negative, it is normalised to `length s - index`, --- | where `s` is the input string. `Nothing` is returned if either --- | index is out of bounds or if `begin > end` after normalisation. --- | --- | ```purescript --- | slice 0 0 "purescript" == Just "" --- | slice 0 1 "purescript" == Just "p" --- | slice 3 6 "purescript" == Just "esc" --- | slice (-4) (-1) "purescript" == Just "rip" --- | slice (-4) 3 "purescript" == Nothing --- | ``` -slice :: Int -> Int -> String -> Maybe String -slice b e s = if b' < 0 || b' >= l || - e' < 0 || e' >= l || - b' > e' - then Nothing - else Just (_slice b e s) - where - l = length s - norm x | x < 0 = l + x - | otherwise = x - b' = norm b - e' = norm e - -foreign import _slice :: Int -> Int -> String -> String - -- | If the string starts with the given prefix, return the portion of the -- | string left after removing it, as a Just value. Otherwise, return Nothing. -- | @@ -207,11 +39,10 @@ foreign import _slice :: Int -> Int -> String -> String -- | stripPrefix (Pattern "http:") "http://purescript.org" == Just "//purescript.org" -- | stripPrefix (Pattern "http:") "https://purescript.org" == Nothing -- | ``` --- | stripPrefix :: Pattern -> String -> Maybe String stripPrefix prefix@(Pattern prefixS) str = - case indexOf prefix str of - Just 0 -> Just $ drop (length prefixS) str + case SCU.indexOf prefix str of + Just 0 -> Just $ SCU.drop (SCU.length prefixS) str _ -> Nothing -- | If the string ends with the given suffix, return the portion of the @@ -222,116 +53,20 @@ stripPrefix prefix@(Pattern prefixS) str = -- | stripSuffix (Pattern ".exe") "psc.exe" == Just "psc" -- | stripSuffix (Pattern ".exe") "psc" == Nothing -- | ``` --- | stripSuffix :: Pattern -> String -> Maybe String stripSuffix suffix@(Pattern suffixS) str = - case lastIndexOf suffix str of - Just x | x == length str - length suffixS -> Just $ take x str + case SCU.lastIndexOf suffix str of + Just x | x == SCU.length str - SCU.length suffixS -> Just $ SCU.take x str _ -> Nothing --- | Converts an array of characters into a string. --- | --- | ```purescript --- | fromCharArray ['H', 'e', 'l', 'l', 'o'] == "Hello" --- | ``` --- | -foreign import fromCharArray :: Array Char -> String - -- | Checks whether the pattern appears in the given string. -- | -- | ```purescript -- | contains (Pattern "needle") "haystack with needle" == true -- | contains (Pattern "needle") "haystack" == false -- | ``` --- | contains :: Pattern -> String -> Boolean -contains pat = isJust <<< indexOf pat - --- | Returns the index of the first occurrence of the pattern in the --- | given string. Returns `Nothing` if there is no match. --- | --- | ```purescript --- | indexOf (Pattern "c") "abcdc" == Just 2 --- | indexOf (Pattern "c") "aaa" == Nothing --- | ``` --- | -indexOf :: Pattern -> String -> Maybe Int -indexOf = _indexOf Just Nothing - -foreign import _indexOf - :: (forall a. a -> Maybe a) - -> (forall a. Maybe a) - -> Pattern - -> String - -> Maybe Int - --- | Returns the index of the first occurrence of the pattern in the --- | given string, starting at the specified index. Returns `Nothing` if there is --- | no match. --- | --- | ```purescript --- | indexOf' (Pattern "a") 2 "ababa" == Just 2 --- | indexOf' (Pattern "a") 3 "ababa" == Just 4 --- | ``` --- | -indexOf' :: Pattern -> Int -> String -> Maybe Int -indexOf' = _indexOf' Just Nothing - -foreign import _indexOf' - :: (forall a. a -> Maybe a) - -> (forall a. Maybe a) - -> Pattern - -> Int - -> String - -> Maybe Int - --- | Returns the index of the last occurrence of the pattern in the --- | given string. Returns `Nothing` if there is no match. --- | --- | ```purescript --- | lastIndexOf (Pattern "c") "abcdc" == Just 4 --- | lastIndexOf (Pattern "c") "aaa" == Nothing --- | ``` --- | -lastIndexOf :: Pattern -> String -> Maybe Int -lastIndexOf = _lastIndexOf Just Nothing - -foreign import _lastIndexOf - :: (forall a. a -> Maybe a) - -> (forall a. Maybe a) - -> Pattern - -> String - -> Maybe Int - --- | Returns the index of the last occurrence of the pattern in the --- | given string, starting at the specified index --- | and searching backwards towards the beginning of the string. --- | Returns `Nothing` if there is no match. --- | --- | ```purescript --- | lastIndexOf' (Pattern "a") 1 "ababa" == Just 0 --- | lastIndexOf' (Pattern "a") 3 "ababa" == Just 2 --- | lastIndexOf' (Pattern "a") 4 "ababa" == Just 4 --- | ``` --- | -lastIndexOf' :: Pattern -> Int -> String -> Maybe Int -lastIndexOf' = _lastIndexOf' Just Nothing - -foreign import _lastIndexOf' - :: (forall a. a -> Maybe a) - -> (forall a. Maybe a) - -> Pattern - -> Int - -> String - -> Maybe Int - --- | Returns the number of characters the string is composed of. --- | --- | ```purescript --- | length "Hello World" == 11 --- | ``` --- | -foreign import length :: String -> Int +contains pat = isJust <<< SCU.indexOf pat -- | Compare two strings in a locale-aware fashion. This is in contrast to -- | the `Ord` instance on `String` which treats strings as arrays of code @@ -341,7 +76,6 @@ foreign import length :: String -> Int -- | "Γ€" `localeCompare` "b" == LT -- | "Γ€" `compare` "b" == GT -- | ``` --- | localeCompare :: String -> String -> Ordering localeCompare = _localeCompare LT EQ GT @@ -358,7 +92,6 @@ foreign import _localeCompare -- | ```purescript -- | replace (Pattern "<=") (Replacement "≀") "a <= b <= c" == "a ≀ b <= c" -- | ``` --- | foreign import replace :: Pattern -> Replacement -> String -> String -- | Replaces all occurences of the pattern with the replacement string. @@ -366,96 +99,21 @@ foreign import replace :: Pattern -> Replacement -> String -> String -- | ```purescript -- | replaceAll (Pattern "<=") (Replacement "≀") "a <= b <= c" == "a ≀ b ≀ c" -- | ``` --- | foreign import replaceAll :: Pattern -> Replacement -> String -> String --- | Returns the first `n` characters of the string. --- | --- | ```purescript --- | take 5 "Hello World" == "Hello" --- | ``` --- | -foreign import take :: Int -> String -> String - --- | Returns the last `n` characters of the string. --- | --- | ```purescript --- | takeRight 5 "Hello World" == "World" --- | ``` --- | -takeRight :: Int -> String -> String -takeRight i s = drop (length s - i) s - --- | Returns the string without the first `n` characters. --- | --- | ```purescript --- | drop 6 "Hello World" == "World" --- | ``` --- | -foreign import drop :: Int -> String -> String - --- | Returns the string without the last `n` characters. --- | --- | ```purescript --- | dropRight 6 "Hello World" == "Hello" --- | ``` --- | -dropRight :: Int -> String -> String -dropRight i s = take (length s - i) s - --- | Returns the number of contiguous characters at the beginning --- | of the string for which the predicate holds. --- | --- | ```purescript --- | countPrefix (_ /= ' ') "Hello World" == 5 -- since length "Hello" == 5 --- | ``` --- | -foreign import countPrefix :: (Char -> Boolean) -> String -> Int - -- | Returns the substrings of the second string separated along occurences -- | of the first string. -- | -- | ```purescript -- | split (Pattern " ") "hello world" == ["hello", "world"] -- | ``` --- | foreign import split :: Pattern -> String -> Array String --- | 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" == { before: "He", after: "llo World"} --- | splitAt 10 "Hi" == { before: "Hi", after: ""} --- | ``` --- | --- | 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. --- | --- | ```purescript --- | toCharArray "Hello☺\n" == ['H','e','l','l','o','☺','\n'] --- | ``` --- | -foreign import toCharArray :: String -> Array Char - -- | Returns the argument converted to lowercase. -- | -- | ```purescript -- | toLower "hElLo" == "hello" -- | ``` --- | foreign import toLower :: String -> String -- | Returns the argument converted to uppercase. @@ -463,7 +121,6 @@ foreign import toLower :: String -> String -- | ```purescript -- | toUpper "Hello" == "HELLO" -- | ``` --- | foreign import toUpper :: String -> String -- | Removes whitespace from the beginning and end of a string, including @@ -473,7 +130,6 @@ foreign import toUpper :: String -> String -- | ```purescript -- | trim " Hello \n World\n\t " == "Hello \n World" -- | ``` --- | foreign import trim :: String -> String -- | Joins the strings in the array together, inserting the first argument @@ -482,5 +138,4 @@ foreign import trim :: String -> String -- | ```purescript -- | joinWith ", " ["apple", "banana", "orange"] == "apple, banana, orange" -- | ``` --- | foreign import joinWith :: String -> Array String -> String diff --git a/src/Data/String/CodePoint.purs b/src/Data/String/CodePoint.purs new file mode 100644 index 0000000..b8cec10 --- /dev/null +++ b/src/Data/String/CodePoint.purs @@ -0,0 +1,446 @@ +-- | These functions allow PureScript strings to be treated as if they were +-- | sequences of Unicode code points instead of their true underlying +-- | implementation (sequences of UTF-16 code units). For nearly all uses of +-- | strings, these functions should be preferred over the ones in `Data.String`. +module Data.String.CodePoints + ( CodePoint + , codePointAt + , fromInt + , toInt + , fromChar + , toCodePointArray + , fromCodePointArray + , countPrefix + , drop + , dropWhile + , indexOf + , indexOf' + , lastIndexOf + , lastIndexOf' + , length + , singleton + , splitAt + , take + , takeWhile + , uncons + ) where + +import Prelude + +import Data.Array as Array +import Data.Char (toCharCode) +import Data.Char as Char +import Data.Int (hexadecimal, toStringAs) +import Data.Maybe (Maybe(..)) +import Data.String as String +import Data.String.Unsafe as Unsafe +import Data.Tuple (Tuple(..)) +import Data.Unfoldable (unfoldr) + +-- | CodePoint is an Int bounded between 0 and 0x10FFFF, corresponding to +-- | Unicode code points. +newtype CodePoint = CodePoint Int + +derive instance eqCodePoint :: Eq CodePoint +derive instance ordCodePoint :: Ord CodePoint + +instance showCodePoint :: Show CodePoint where + show (CodePoint i) = "(CodePoint 0x" <> String.toUpper (toStringAs hexadecimal i) <> ")" + +-- I would prefer that this smart constructor not need to exist and instead +-- CodePoint just implements Enum, but the Enum module already depends on this +-- one. To avoid the circular dependency, we just expose these two functions. +-- | +-- | ```purescript +-- | >>> it = fromInt 0x1D400 -- U+1D400 MATHEMATICAL BOLD CAPITAL A +-- | Just (CodePoint 0x1D400) +-- | +-- | >>> map singleton it +-- | Just "𝐀" +-- | +-- | >>> fromInt 0x110000 -- does not correspond to a Unicode code point +-- | Nothing +-- | ``` +-- | +fromInt :: Int -> Maybe CodePoint +fromInt n | 0 <= n && n <= 0x10FFFF = Just (CodePoint n) +fromInt n = Nothing + +-- | +-- | ```purescript +-- | >>> toInt (fromChar 'B') +-- | 66 +-- | +-- | >>> boldA = fromInt 0x1D400 +-- | >>> boldA +-- | Just (CodePoint 0x1D400) +-- | >>> map toInt boldA +-- | Just 119808 -- is the same as 0x1D400 +-- | ``` +-- | +toInt :: CodePoint -> Int +toInt (CodePoint n) = n + +-- | Creates a CodePoint from a given Char. +-- | +-- | ```purescript +-- | >>> fromChar 'B' +-- | CodePoint 0x42 -- represents 'B' +-- | ``` +-- | +fromChar :: Char -> CodePoint +fromChar = toCharCode >>> CodePoint + +unsurrogate :: Int -> Int -> CodePoint +unsurrogate lead trail = CodePoint ((lead - 0xD800) * 0x400 + (trail - 0xDC00) + 0x10000) + +isLead :: Int -> Boolean +isLead cu = 0xD800 <= cu && cu <= 0xDBFF + +isTrail :: Int -> Boolean +isTrail cu = 0xDC00 <= cu && cu <= 0xDFFF + +fromCharCode :: Int -> String +fromCharCode = String.singleton <<< Char.fromCharCode + +-- WARN: this function expects the String parameter to be non-empty +unsafeCodePointAt0 :: String -> CodePoint +unsafeCodePointAt0 = _unsafeCodePointAt0 unsafeCodePointAt0Fallback + +foreign import _unsafeCodePointAt0 + :: (String -> CodePoint) + -> String + -> CodePoint + +unsafeCodePointAt0Fallback :: String -> CodePoint +unsafeCodePointAt0Fallback s = + let cu0 = Unsafe.charCodeAt 0 s in + let cu1 = Unsafe.charCodeAt 1 s in + if isLead cu0 && isTrail cu1 + then unsurrogate cu0 cu1 + else CodePoint cu0 + + +-- | Returns the first code point of the string after dropping the given number +-- | of code points from the beginning, if there is such a code point. Operates +-- | in constant space and in time linear to the given index. +-- | +-- | ```purescript +-- | >>> codePointAt 1 "𝐀𝐀𝐀𝐀" +-- | Just (CodePoint 0x1D400) -- represents "𝐀" +-- | -- compare to Data.String: +-- | >>> charAt 1 "𝐀𝐀𝐀𝐀" +-- | Just 'οΏ½' +-- | ``` +-- | +codePointAt :: Int -> String -> Maybe CodePoint +codePointAt n _ | n < 0 = Nothing +codePointAt 0 "" = Nothing +codePointAt 0 s = Just (unsafeCodePointAt0 s) +codePointAt n s = _codePointAt codePointAtFallback Just Nothing unsafeCodePointAt0 n s + +foreign import _codePointAt + :: (Int -> String -> Maybe CodePoint) + -> (forall a. a -> Maybe a) + -> (forall a. Maybe a) + -> (String -> CodePoint) + -> Int + -> String + -> Maybe CodePoint + +codePointAtFallback :: Int -> String -> Maybe CodePoint +codePointAtFallback n s = case uncons s of + Just { head, tail } -> if n == 0 then Just head else codePointAtFallback (n - 1) tail + _ -> Nothing + + +-- | Returns the number of code points in the leading sequence of code points +-- | which all match the given predicate. Operates in constant space and in +-- | time linear to the length of the string. +-- | +-- | ```purescript +-- | >>> countPrefix (\c -> toInt c == 0x1D400) "𝐀𝐀 b c 𝐀" +-- | 2 +-- | ``` +-- | +countPrefix :: (CodePoint -> Boolean) -> String -> Int +countPrefix = _countPrefix countFallback unsafeCodePointAt0 + +foreign import _countPrefix + :: ((CodePoint -> Boolean) -> String -> Int) + -> (String -> CodePoint) + -> (CodePoint -> Boolean) + -> String + -> Int + +countFallback :: (CodePoint -> Boolean) -> String -> Int +countFallback p s = countTail p s 0 + +countTail :: (CodePoint -> Boolean) -> String -> Int -> Int +countTail p s accum = case uncons s of + Just { head, tail } -> if p head then countTail p tail (accum + 1) else accum + _ -> accum + + +-- | Drops the given number of code points from the beginning of the string. If +-- | the string does not have that many code points, returns the empty string. +-- | Operates in constant space and in time linear to the given number. +-- | +-- | ```purescript +-- | >>> drop 5 "𝐀𝐀 b c" +-- | "c" +-- | -- compared to Data.String: +-- | >>> drop 5 "𝐀𝐀 b c" +-- | "b c" -- because "𝐀" occupies 2 code units +-- | ``` +-- | +drop :: Int -> String -> String +drop n s = String.drop (String.length (take n s)) s + + +-- | Drops the leading sequence of code points which all match the given +-- | predicate from the string. Operates in constant space and in time linear +-- | to the length of the string. +-- | +-- | ```purescript +-- | >>> dropWhile (\c -> toInt c == 0x1D400) "𝐀𝐀 b c 𝐀" +-- | " b c 𝐀" +-- | ``` +-- | +dropWhile :: (CodePoint -> Boolean) -> String -> String +dropWhile p s = drop (countPrefix p s) s + + +-- | Creates a string from an array of code points. Operates in space and time +-- | linear to the length of the array. +-- | +-- | ```purescript +-- | >>> codePointArray = toCodePointArray "c 𝐀" +-- | >>> codePointArray +-- | [CodePoint 0x63, CodePoint 0x20, CodePoint 0x1D400] +-- | >>> fromCodePointArray codePointArray +-- | "c 𝐀" +-- | ``` +-- | +fromCodePointArray :: Array CodePoint -> String +fromCodePointArray = _fromCodePointArray singletonFallback + +foreign import _fromCodePointArray + :: (CodePoint -> String) + -> Array CodePoint + -> String + +-- | Returns the number of code points preceding the first match of the given +-- | pattern in the string. Returns Nothing when no matches are found. +-- | +-- | ```purescript +-- | >>> indexOf (Pattern "𝐀") "b 𝐀𝐀 c 𝐀" +-- | Just 2 +-- | >>> indexOf (Pattern "o") "b 𝐀𝐀 c 𝐀" +-- | Nothing +-- | ``` +-- | +indexOf :: String.Pattern -> String -> Maybe Int +indexOf p s = (\i -> length (String.take i s)) <$> String.indexOf p s + + +-- | Returns the number of code points preceding the first match of the given +-- | pattern in the string. Pattern matches preceding the given index will be +-- | ignored. Returns Nothing when no matches are found. +-- | +-- | ```purescript +-- | >>> indexOf' (Pattern "𝐀") 4 "b 𝐀𝐀 c 𝐀" +-- | Just 7 +-- | >>> indexOf' (Pattern "o") 4 "b 𝐀𝐀 c 𝐀" +-- | Nothing +-- | ``` +-- | +indexOf' :: String.Pattern -> Int -> String -> Maybe Int +indexOf' p i s = + let s' = drop i s in + (\k -> i + length (String.take k s')) <$> String.indexOf p s' + + +-- | Returns the number of code points preceding the last match of the given +-- | pattern in the string. Returns Nothing when no matches are found. +-- | +-- | ```purescript +-- | >>> lastIndexOf (Pattern "𝐀") "b 𝐀𝐀 c 𝐀" +-- | Just 7 +-- | >>> lastIndexOf (Pattern "o") "b 𝐀𝐀 c 𝐀" +-- | Nothing +-- | ``` +-- | +lastIndexOf :: String.Pattern -> String -> Maybe Int +lastIndexOf p s = (\i -> length (String.take i s)) <$> String.lastIndexOf p s + + +-- | Returns the number of code points preceding the first match of the given +-- | pattern in the string. Pattern matches following the given index will be +-- | ignored. Returns Nothing when no matches are found. +-- | +-- | ```purescript +-- | >>> lastIndexOf' (Pattern "𝐀") 5 "b 𝐀𝐀 c 𝐀" +-- | Just 3 +-- | >>> lastIndexOf' (Pattern "o") 5 "b 𝐀𝐀 c 𝐀" +-- | Nothing +-- | ``` +-- | +lastIndexOf' :: String.Pattern -> Int -> String -> Maybe Int +lastIndexOf' p i s = + let i' = String.length (take i s) in + (\k -> length (String.take k s)) <$> String.lastIndexOf' p i' s + + +-- | Returns the number of code points in the string. Operates in constant +-- | space and in time linear to the length of the string. +-- | +-- | ```purescript +-- | >>> length "b 𝐀𝐀 c 𝐀" +-- | 8 +-- | -- compare to Data.String: +-- | >>> length "b 𝐀𝐀 c 𝐀" +-- | 11 +-- | ``` +-- | +length :: String -> Int +length = Array.length <<< toCodePointArray + + +-- | Creates a string containing just the given code point. Operates in +-- | constant space and time. +-- | +-- | ```purescript +-- | >>> map singleton (fromInt 0x1D400) +-- | Just "𝐀" +-- | ``` +-- | +singleton :: CodePoint -> String +singleton = _singleton singletonFallback + +foreign import _singleton + :: (CodePoint -> String) + -> CodePoint + -> String + +singletonFallback :: CodePoint -> String +singletonFallback (CodePoint cp) | cp <= 0xFFFF = fromCharCode cp +singletonFallback (CodePoint cp) = + let lead = ((cp - 0x10000) / 0x400) + 0xD800 in + let trail = (cp - 0x10000) `mod` 0x400 + 0xDC00 in + fromCharCode lead <> fromCharCode trail + + +-- | 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 𝐀" } +-- | ``` +-- | +-- | 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 + { 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 +-- | points, returns the empty string. Operates in constant space and in time +-- | linear to the given number. +-- | +-- | ```purescript +-- | >>> take 3 "b 𝐀𝐀 c 𝐀" +-- | "b 𝐀" +-- | -- compare to Data.String: +-- | >>> take 3 "b 𝐀𝐀 c 𝐀" +-- | "b οΏ½" +-- | ``` +-- | +take :: Int -> String -> String +take = _take takeFallback + +foreign import _take :: (Int -> String -> String) -> Int -> String -> String + +takeFallback :: Int -> String -> String +takeFallback n _ | n < 1 = "" +takeFallback n s = case uncons s of + Just { head, tail } -> singleton head <> takeFallback (n - 1) tail + _ -> s + + +-- | Returns a string containing the leading sequence of code points which all +-- | match the given predicate from the string. Operates in constant space and +-- | in time linear to the length of the string. +-- | +-- | ```purescript +-- | >>> takeWhile (\c -> toInt c == 0x1D400) "𝐀𝐀 b c 𝐀" +-- | "𝐀𝐀" +-- | ``` +-- | +takeWhile :: (CodePoint -> Boolean) -> String -> String +takeWhile p s = take (countPrefix p s) s + + +-- | Creates an array of code points from a string. Operates in space and time +-- | linear to the length of the string. +-- | +-- | ```purescript +-- | >>> codePointArray = toCodePointArray "b 𝐀𝐀" +-- | >>> codePointArray +-- | [CodePoint 0x62, CodePoint 0x20, CodePoint 0x1D400, CodePoint 0x1D400] +-- | >>> map singleton codePointArray +-- | ["b", " ", "𝐀", "𝐀"] +-- | ``` +-- | +toCodePointArray :: String -> Array CodePoint +toCodePointArray = _toCodePointArray toCodePointArrayFallback unsafeCodePointAt0 + +foreign import _toCodePointArray + :: (String -> Array CodePoint) + -> (String -> CodePoint) + -> String + -> Array CodePoint + +toCodePointArrayFallback :: String -> Array CodePoint +toCodePointArrayFallback s = unfoldr unconsButWithTuple s + +unconsButWithTuple :: String -> Maybe (Tuple CodePoint String) +unconsButWithTuple s = (\{ head, tail } -> Tuple head tail) <$> uncons s + + +-- | Returns a record with the first code point and the remaining code points +-- | of the string. Returns Nothing if the string is empty. Operates in +-- | constant space and time. +-- | +-- | ```purescript +-- | >>> uncons "𝐀𝐀 c 𝐀" +-- | Just { head: CodePoint 0x1D400, tail: "𝐀 c 𝐀" } +-- | >>> uncons "" +-- | Nothing +-- | ``` +-- | +uncons :: String -> Maybe { head :: CodePoint, tail :: String } +uncons s = case String.length s of + 0 -> Nothing + 1 -> Just { head: CodePoint (Unsafe.charCodeAt 0 s), tail: "" } + _ -> + let cu0 = Unsafe.charCodeAt 0 s in + let cu1 = Unsafe.charCodeAt 1 s in + if isLead cu0 && isTrail cu1 + then Just { head: unsurrogate cu0 cu1, tail: String.drop 2 s } + else Just { head: CodePoint cu0, tail: String.drop 1 s } diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index d51d180..f6c512b 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -3,16 +3,16 @@ -- | implementation (sequences of UTF-16 code units). For nearly all uses of -- | strings, these functions should be preferred over the ones in `Data.String`. module Data.String.CodePoints - ( module StringReExports - , CodePoint() + ( CodePoint , codePointAt , codePointFromInt , codePointToInt , codePointFromChar + , toCodePointArray + , fromCodePointArray , countPrefix , drop , dropWhile - , fromCodePointArray , indexOf , indexOf' , lastIndexOf @@ -22,7 +22,6 @@ module Data.String.CodePoints , splitAt , take , takeWhile - , toCodePointArray , uncons ) where @@ -32,20 +31,12 @@ import Data.Array as Array import Data.Char (toCharCode) import Data.Char as Char import Data.Int (hexadecimal, toStringAs) -import Data.Maybe (Maybe(Just, Nothing)) +import Data.Maybe (Maybe(..)) import Data.String as String import Data.String.Unsafe as Unsafe --- WARN: If a new function is added to Data.String, a version of that function --- should be exported from this module, which should be the same except that it --- should operate on the code point level rather than the code unit level. If --- the function's behaviour does not change based on whether we consider --- strings as sequences of code points or code units, it can simply be --- re-exported from Data.String. -import Data.String (Pattern(..), Replacement(..), charAt, charCodeAt, contains, fromCharArray, joinWith, localeCompare, null, replace, replaceAll, split, stripPrefix, stripSuffix, toChar, toCharArray, toLower, toUpper, trim) as StringReExports -import Data.Tuple (Tuple(Tuple)) +import Data.Tuple (Tuple(..)) import Data.Unfoldable (unfoldr) - -- | CodePoint is an Int bounded between 0 and 0x10FFFF, corresponding to -- | Unicode code points. newtype CodePoint = CodePoint Int diff --git a/src/Data/String/CodeUnits.js b/src/Data/String/CodeUnits.js new file mode 100644 index 0000000..425bb47 --- /dev/null +++ b/src/Data/String/CodeUnits.js @@ -0,0 +1,119 @@ +"use strict"; + +exports.fromCharArray = function (a) { + return a.join(""); +}; + +exports.singleton = function (c) { + return c; +}; + +exports._charAt = function (just) { + return function (nothing) { + return function (i) { + return function (s) { + return i >= 0 && i < s.length ? just(s.charAt(i)) : nothing; + }; + }; + }; +}; + +exports._toChar = function (just) { + return function (nothing) { + return function (s) { + return s.length === 1 ? just(s) : nothing; + }; + }; +}; + +exports.toCharArray = function (s) { + return s.split(""); +}; + +exports.length = function (s) { + return s.length; +}; + +exports.countPrefix = function (p) { + return function (s) { + var i = 0; + while (i < s.length && p(s.charAt(i))) i++; + return i; + }; +}; + +exports._indexOf = function (just) { + return function (nothing) { + return function (x) { + return function (s) { + var i = s.indexOf(x); + return i === -1 ? nothing : just(i); + }; + }; + }; +}; + +exports["_indexOf'"] = function (just) { + return function (nothing) { + 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); + }; + }; + }; + }; +}; + +exports._lastIndexOf = function (just) { + return function (nothing) { + return function (x) { + return function (s) { + var i = s.lastIndexOf(x); + return i === -1 ? nothing : just(i); + }; + }; + }; +}; + +exports["_lastIndexOf'"] = function (just) { + return function (nothing) { + 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); + }; + }; + }; + }; +}; + +exports.take = function (n) { + return function (s) { + return s.substr(0, n); + }; +}; + +exports.drop = function (n) { + return function (s) { + return s.substring(n); + }; +}; + +exports._slice = function (b) { + return function (e) { + return function (s) { + return s.slice(b,e); + }; + }; +}; + +exports.splitAt = function (i) { + return function (s) { + return { before: s.substring(0, i), after: s.substring(i) }; + }; +}; diff --git a/src/Data/String/CodeUnits.purs b/src/Data/String/CodeUnits.purs new file mode 100644 index 0000000..3c5ac9c --- /dev/null +++ b/src/Data/String/CodeUnits.purs @@ -0,0 +1,294 @@ +-- | Wraps the functions of Javascript's `String` object. +-- | A String represents a sequence of characters. +-- | For details of the underlying implementation, see [String Reference at MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String). +module Data.String.CodeUnits + ( singleton + , fromCharArray + , charAt + , toChar + , toCharArray + , uncons + , length + , countPrefix + , indexOf + , indexOf' + , lastIndexOf + , lastIndexOf' + , take + , takeRight + , takeWhile + , drop + , dropRight + , dropWhile + , slice + , splitAt + ) where + +import Prelude + +import Data.Maybe (Maybe(..)) +import Data.String.Pattern (Pattern) +import Data.String.Unsafe as U + +-- | Returns a string of length `1` containing the given character. +-- | +-- | ```purescript +-- | singleton 'l' == "l" +-- | ``` +-- | +foreign import singleton :: Char -> String + +-- | Converts an array of characters into a string. +-- | +-- | ```purescript +-- | fromCharArray ['H', 'e', 'l', 'l', 'o'] == "Hello" +-- | ``` +foreign import fromCharArray :: Array Char -> String + +-- | Returns the character at the given index, if the index is within bounds. +-- | +-- | ```purescript +-- | charAt 2 "Hello" == Just 'l' +-- | charAt 10 "Hello" == Nothing +-- | ``` +-- | +charAt :: Int -> String -> Maybe Char +charAt = _charAt Just Nothing + +foreign import _charAt + :: (forall a. a -> Maybe a) + -> (forall a. Maybe a) + -> Int + -> String + -> Maybe Char + +-- | Converts the string to a character, if the length of the string is +-- | exactly `1`. +-- | +-- | ```purescript +-- | toChar "l" == Just 'l' +-- | toChar "Hi" == Nothing -- since length is not 1 +-- | ``` +toChar :: String -> Maybe Char +toChar = _toChar Just Nothing + +foreign import _toChar + :: (forall a. a -> Maybe a) + -> (forall a. Maybe a) + -> String + -> Maybe Char + +-- | Converts the string into an array of characters. +-- | +-- | ```purescript +-- | toCharArray "Hello☺\n" == ['H','e','l','l','o','☺','\n'] +-- | ``` +foreign import toCharArray :: String -> Array Char + +-- | Returns the first character and the rest of the string, +-- | if the string is not empty. +-- | +-- | ```purescript +-- | uncons "" == Nothing +-- | uncons "Hello World" == Just { head: 'H', tail: "ello World" } +-- | ``` +-- | +uncons :: String -> Maybe { head :: Char, tail :: String } +uncons "" = Nothing +uncons s = Just { head: U.charAt zero s, tail: drop one s } + +-- | Returns the number of characters the string is composed of. +-- | +-- | ```purescript +-- | length "Hello World" == 11 +-- | ``` +-- | +foreign import length :: String -> Int + +-- | Returns the number of contiguous characters at the beginning +-- | of the string for which the predicate holds. +-- | +-- | ```purescript +-- | countPrefix (_ /= ' ') "Hello World" == 5 -- since length "Hello" == 5 +-- | ``` +-- | +foreign import countPrefix :: (Char -> Boolean) -> String -> Int + +-- | Returns the index of the first occurrence of the pattern in the +-- | given string. Returns `Nothing` if there is no match. +-- | +-- | ```purescript +-- | indexOf (Pattern "c") "abcdc" == Just 2 +-- | indexOf (Pattern "c") "aaa" == Nothing +-- | ``` +-- | +indexOf :: Pattern -> String -> Maybe Int +indexOf = _indexOf Just Nothing + +foreign import _indexOf + :: (forall a. a -> Maybe a) + -> (forall a. Maybe a) + -> Pattern + -> String + -> Maybe Int + +-- | Returns the index of the first occurrence of the pattern in the +-- | given string, starting at the specified index. Returns `Nothing` if there is +-- | no match. +-- | +-- | ```purescript +-- | indexOf' (Pattern "a") 2 "ababa" == Just 2 +-- | indexOf' (Pattern "a") 3 "ababa" == Just 4 +-- | ``` +-- | +indexOf' :: Pattern -> Int -> String -> Maybe Int +indexOf' = _indexOf' Just Nothing + +foreign import _indexOf' + :: (forall a. a -> Maybe a) + -> (forall a. Maybe a) + -> Pattern + -> Int + -> String + -> Maybe Int + +-- | Returns the index of the last occurrence of the pattern in the +-- | given string. Returns `Nothing` if there is no match. +-- | +-- | ```purescript +-- | lastIndexOf (Pattern "c") "abcdc" == Just 4 +-- | lastIndexOf (Pattern "c") "aaa" == Nothing +-- | ``` +-- | +lastIndexOf :: Pattern -> String -> Maybe Int +lastIndexOf = _lastIndexOf Just Nothing + +foreign import _lastIndexOf + :: (forall a. a -> Maybe a) + -> (forall a. Maybe a) + -> Pattern + -> String + -> Maybe Int + +-- | Returns the index of the last occurrence of the pattern in the +-- | given string, starting at the specified index +-- | and searching backwards towards the beginning of the string. +-- | Returns `Nothing` if there is no match. +-- | +-- | ```purescript +-- | lastIndexOf' (Pattern "a") 1 "ababa" == Just 0 +-- | lastIndexOf' (Pattern "a") 3 "ababa" == Just 2 +-- | lastIndexOf' (Pattern "a") 4 "ababa" == Just 4 +-- | ``` +-- | +lastIndexOf' :: Pattern -> Int -> String -> Maybe Int +lastIndexOf' = _lastIndexOf' Just Nothing + +foreign import _lastIndexOf' + :: (forall a. a -> Maybe a) + -> (forall a. Maybe a) + -> Pattern + -> Int + -> String + -> Maybe Int + +-- | Returns the first `n` characters of the string. +-- | +-- | ```purescript +-- | take 5 "Hello World" == "Hello" +-- | ``` +-- | +foreign import take :: Int -> String -> String + +-- | Returns the last `n` characters of the string. +-- | +-- | ```purescript +-- | takeRight 5 "Hello World" == "World" +-- | ``` +-- | +takeRight :: Int -> String -> String +takeRight i s = drop (length s - i) s + +-- | Returns the longest prefix (possibly empty) of characters that satisfy +-- | the predicate. +-- | +-- | ```purescript +-- | takeWhile (_ /= ':') "http://purescript.org" == "http" +-- | ``` +-- | +takeWhile :: (Char -> Boolean) -> String -> String +takeWhile p s = take (countPrefix p s) s + +-- | Returns the string without the first `n` characters. +-- | +-- | ```purescript +-- | drop 6 "Hello World" == "World" +-- | ``` +-- | +foreign import drop :: Int -> String -> String + +-- | Returns the string without the last `n` characters. +-- | +-- | ```purescript +-- | dropRight 6 "Hello World" == "Hello" +-- | ``` +-- | +dropRight :: Int -> String -> String +dropRight i s = take (length s - i) s + +-- | Returns the suffix remaining after `takeWhile`. +-- | +-- | ```purescript +-- | dropWhile (_ /= '.') "Test.purs" == ".purs" +-- | ``` +-- | +dropWhile :: (Char -> Boolean) -> String -> String +dropWhile p s = drop (countPrefix p s) s + +-- | Returns the substring at indices `[begin, end)`. +-- | If either index is negative, it is normalised to `length s - index`, +-- | where `s` is the input string. `Nothing` is returned if either +-- | index is out of bounds or if `begin > end` after normalisation. +-- | +-- | ```purescript +-- | slice 0 0 "purescript" == Just "" +-- | slice 0 1 "purescript" == Just "p" +-- | slice 3 6 "purescript" == Just "esc" +-- | slice (-4) (-1) "purescript" == Just "rip" +-- | slice (-4) 3 "purescript" == Nothing +-- | ``` +slice :: Int -> Int -> String -> Maybe String +slice b e s = if b' < 0 || b' >= l || + e' < 0 || e' >= l || + b' > e' + then Nothing + else Just (_slice b e s) + where + l = length s + norm x | x < 0 = l + x + | otherwise = x + b' = norm b + e' = norm e + +foreign import _slice :: Int -> Int -> String -> String + +-- | 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" == { before: "He", after: "llo World"} +-- | splitAt 10 "Hi" == { before: "Hi", after: ""} +-- | ``` +-- | +-- | 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 } diff --git a/src/Data/String/Gen.purs b/src/Data/String/Gen.purs index 08c902a..845b5e8 100644 --- a/src/Data/String/Gen.purs +++ b/src/Data/String/Gen.purs @@ -5,13 +5,13 @@ import Prelude import Control.Monad.Gen (class MonadGen, chooseInt, unfoldable, sized, resize) import Control.Monad.Rec.Class (class MonadRec) import Data.Char.Gen as CG -import Data.String as S +import Data.String.CodeUnits as SCU -- | Generates a string using the specified character generator. genString :: forall m. MonadRec m => MonadGen m => m Char -> m String genString genChar = sized \size -> do newSize <- chooseInt 1 (max 1 size) - resize (const newSize) $ S.fromCharArray <$> unfoldable genChar + resize (const newSize) $ SCU.fromCharArray <$> unfoldable genChar -- | Generates a string using characters from the Unicode basic multilingual -- | plain. @@ -41,4 +41,3 @@ genAlphaLowercaseString = genString CG.genAlphaLowercase -- | Generates a string using uppercase characters from the basic Latin alphabet. genAlphaUppercaseString :: forall m. MonadRec m => MonadGen m => m String genAlphaUppercaseString = genString CG.genAlphaUppercase - diff --git a/src/Data/String/NonEmpty.purs b/src/Data/String/NonEmpty.purs deleted file mode 100644 index a6f50e2..0000000 --- a/src/Data/String/NonEmpty.purs +++ /dev/null @@ -1,499 +0,0 @@ --- | Non-empty strings. --- | --- | Please note that the examples in this documentation use a notation like --- | `NonEmptyString "..."` for demonstration purposes, `NonEmptyString` cannot --- | be created directly like that, as we can't prove the string is non-empty to --- | the compiler at compile-time. -module Data.String.NonEmpty - ( NonEmptyString - , NonEmptyReplacement(..) - , fromString - , unsafeFromString - , fromCharArray - , fromNonEmptyCharArray - , singleton - , cons - , snoc - , fromFoldable1 - , toString - , toCharArray - , toNonEmptyCharArray - , charAt - , charCodeAt - , toChar - , appendString - , prependString - , contains - , indexOf - , indexOf' - , lastIndexOf - , lastIndexOf' - , uncons - , length - , localeCompare - , replace - , replaceAll - , take - , takeRight - , takeWhile - , drop - , dropRight - , dropWhile - , stripPrefix - , stripSuffix - , countPrefix - , splitAt - , toLower - , toUpper - , trim - , joinWith - , join1With - , joinWith1 - , module Data.String - ) where - -import Prelude - -import Data.Array.NonEmpty (NonEmptyArray) -import Data.Array.NonEmpty as NEA -import Data.Foldable (class Foldable) -import Data.Foldable as F -import Data.Maybe (Maybe(..), fromJust) -import Data.Semigroup.Foldable (class Foldable1) -import Data.Semigroup.Foldable as F1 -import Data.String (Pattern(..)) -import Data.String as String -import Data.String.Unsafe as U -import Partial.Unsafe (unsafePartial) -import Unsafe.Coerce (unsafeCoerce) - --- | A string that is known not to be empty. -newtype NonEmptyString = NonEmptyString String - -derive newtype instance eqNonEmptyString ∷ Eq NonEmptyString -derive newtype instance ordNonEmptyString ∷ Ord NonEmptyString -derive newtype instance semigroupNonEmptyString ∷ Semigroup NonEmptyString - -instance showNonEmptyString :: Show NonEmptyString where - show (NonEmptyString s) = "(NonEmptyString.unsafeFromString " <> show s <> ")" - --- | A newtype used in cases to specify a non-empty replacement for a pattern. -newtype NonEmptyReplacement = NonEmptyReplacement NonEmptyString - -derive newtype instance eqNonEmptyReplacement :: Eq NonEmptyReplacement -derive newtype instance ordNonEmptyReplacement :: Ord NonEmptyReplacement -derive newtype instance semigroupNonEmptyReplacement ∷ Semigroup NonEmptyReplacement - -instance showNonEmptyReplacement :: Show NonEmptyReplacement where - show (NonEmptyReplacement s) = "(NonEmptyReplacement " <> show s <> ")" - --- | Creates a `NonEmptyString` from a `String`, returning `Nothing` if the --- | input is empty. --- | --- | ```purescript --- | fromString "" = Nothing --- | fromString "hello" = Just (NonEmptyString "hello") --- | ``` -fromString :: String -> Maybe NonEmptyString -fromString = case _ of - "" -> Nothing - s -> Just (NonEmptyString s) - --- | A partial version of `fromString`. -unsafeFromString :: Partial => String -> NonEmptyString -unsafeFromString = fromJust <<< fromString - --- | Creates a `NonEmptyString` from a character array `String`, returning --- | `Nothing` if the input is empty. --- | --- | ```purescript --- | fromCharArray [] = Nothing --- | fromCharArray ['a', 'b', 'c'] = Just (NonEmptyString "abc") --- | ``` -fromCharArray :: Array Char -> Maybe NonEmptyString -fromCharArray = case _ of - [] -> Nothing - cs -> Just (NonEmptyString (String.fromCharArray cs)) - -fromNonEmptyCharArray :: NonEmptyArray Char -> NonEmptyString -fromNonEmptyCharArray = unsafePartial fromJust <<< fromCharArray <<< NEA.toArray - --- | Creates a `NonEmptyString` from a character. -singleton :: Char -> NonEmptyString -singleton = NonEmptyString <<< String.singleton - --- | Creates a `NonEmptyString` from a string by prepending a character. --- | --- | ```purescript --- | cons 'a' "bc" = NonEmptyString "abc" --- | cons 'a' "" = NonEmptyString "a" --- | ``` -cons :: Char -> String -> NonEmptyString -cons c s = NonEmptyString (String.singleton c <> s) - --- | Creates a `NonEmptyString` from a string by appending a character. --- | --- | ```purescript --- | snoc 'c' "ab" = NonEmptyString "abc" --- | snoc 'a' "" = NonEmptyString "a" --- | ``` -snoc :: Char -> String -> NonEmptyString -snoc c s = NonEmptyString (s <> String.singleton c) - --- | Creates a `NonEmptyString` from a `Foldable1` container carrying --- | characters. -fromFoldable1 :: forall f. Foldable1 f => f Char -> NonEmptyString -fromFoldable1 = F1.fold1 <<< coe - where - coe ∷ f Char -> f NonEmptyString - coe = unsafeCoerce - --- | Converts a `NonEmptyString` back into a standard `String`. -toString :: NonEmptyString -> String -toString (NonEmptyString s) = s - --- | Returns the character at the given index, if the index is within bounds. --- | --- | ```purescript --- | charAt 2 (NonEmptyString "Hello") == Just 'l' --- | charAt 10 (NonEmptyString "Hello") == Nothing --- | ``` -charAt :: Int -> NonEmptyString -> Maybe Char -charAt = liftS <<< String.charAt - --- | Returns the numeric Unicode value of the character at the given index, --- | if the index is within bounds. --- | --- | ```purescript --- | charCodeAt 2 (NonEmptyString "5 €") == Just 0x20AC --- | charCodeAt 10 (NonEmptyString "5 €") == Nothing --- | ``` -charCodeAt :: Int -> NonEmptyString -> Maybe Int -charCodeAt = liftS <<< String.charCodeAt - --- | Converts the `NonEmptyString` to a character, if the length of the string --- | is exactly `1`. --- | --- | ```purescript --- | toChar "H" == Just 'H' --- | toChar "Hi" == Nothing --- | ``` -toChar :: NonEmptyString -> Maybe Char -toChar (NonEmptyString s) = String.toChar s - --- | Converts the `NonEmptyString` into an array of characters. --- | --- | ```purescript --- | toCharArray (NonEmptyString "Hello☺\n") == ['H','e','l','l','o','☺','\n'] --- | ``` -toCharArray :: NonEmptyString -> Array Char -toCharArray (NonEmptyString s) = String.toCharArray s - --- | Converts the `NonEmptyString` into a non-empty array of characters. -toNonEmptyCharArray :: NonEmptyString -> NonEmptyArray Char -toNonEmptyCharArray = unsafePartial fromJust <<< NEA.fromArray <<< toCharArray - --- | Appends a string to this non-empty string. Since one of the strings is --- | non-empty we know the result will be too. --- | --- | ```purescript --- | appendString (NonEmptyString "Hello") " world" == NonEmptyString "Hello world" --- | appendString (NonEmptyString "Hello") "" == NonEmptyString "Hello" --- | ``` -appendString :: NonEmptyString -> String -> NonEmptyString -appendString (NonEmptyString s1) s2 = NonEmptyString (s1 <> s2) - --- | Prepends a string to this non-empty string. Since one of the strings is --- | non-empty we know the result will be too. --- | --- | ```purescript --- | prependString "be" (NonEmptyString "fore") == NonEmptyString "before" --- | prependString "" (NonEmptyString "fore") == NonEmptyString "fore" --- | ``` -prependString :: String -> NonEmptyString -> NonEmptyString -prependString s1 (NonEmptyString s2) = NonEmptyString (s1 <> s2) - --- | Returns the first character and the rest of the string. --- | --- | ```purescript --- | uncons "a" == { head: 'a', tail: Nothing } --- | uncons "Hello World" == { head: 'H', tail: Just (NonEmptyString "ello World") } --- | ``` -uncons :: NonEmptyString -> { head :: Char, tail :: Maybe NonEmptyString } -uncons (NonEmptyString s) = - { head: U.charAt 0 s - , tail: fromString (String.drop 1 s) - } - --- | Returns the longest prefix of characters that satisfy the predicate. --- | `Nothing` is returned if there is no matching prefix. --- | --- | ```purescript --- | takeWhile (_ /= ':') (NonEmptyString "http://purescript.org") == Just (NonEmptyString "http") --- | takeWhile (_ == 'a') (NonEmptyString "xyz") == Nothing --- | ``` -takeWhile :: (Char -> Boolean) -> NonEmptyString -> Maybe NonEmptyString -takeWhile f = fromString <<< liftS (String.takeWhile f) - --- | Returns the suffix remaining after `takeWhile`. --- | --- | ```purescript --- | dropWhile (_ /= '.') (NonEmptyString "Test.purs") == Just (NonEmptyString ".purs") --- | ``` -dropWhile :: (Char -> Boolean) -> NonEmptyString -> Maybe NonEmptyString -dropWhile f = fromString <<< liftS (String.dropWhile f) - --- | If the string starts with the given prefix, return the portion of the --- | string left after removing it. If the prefix does not match or there is no --- | remainder, the result will be `Nothing`. --- | --- | ```purescript --- | stripPrefix (Pattern "http:") (NonEmptyString "http://purescript.org") == Just (NonEmptyString "//purescript.org") --- | stripPrefix (Pattern "http:") (NonEmptyString "https://purescript.org") == Nothing --- | stripPrefix (Pattern "Hello!") (NonEmptyString "Hello!") == Nothing --- | ``` -stripPrefix :: Pattern -> NonEmptyString -> Maybe NonEmptyString -stripPrefix pat = fromString <=< liftS (String.stripPrefix pat) - --- | If the string ends with the given suffix, return the portion of the --- | string left after removing it. If the suffix does not match or there is no --- | remainder, the result will be `Nothing`. --- | --- | ```purescript --- | stripSuffix (Pattern ".exe") (NonEmptyString "purs.exe") == Just (NonEmptyString "purs") --- | stripSuffix (Pattern ".exe") (NonEmptyString "purs") == Nothing --- | stripSuffix (Pattern "Hello!") (NonEmptyString "Hello!") == Nothing --- | ``` -stripSuffix :: Pattern -> NonEmptyString -> Maybe NonEmptyString -stripSuffix pat = fromString <=< liftS (String.stripSuffix pat) - --- | Checks whether the pattern appears in the given string. --- | --- | ```purescript --- | contains (Pattern "needle") (NonEmptyString "haystack with needle") == true --- | contains (Pattern "needle") (NonEmptyString "haystack") == false --- | ``` -contains :: Pattern -> NonEmptyString -> Boolean -contains = liftS <<< String.contains - --- | Returns the index of the first occurrence of the pattern in the --- | given string. Returns `Nothing` if there is no match. --- | --- | ```purescript --- | indexOf (Pattern "c") (NonEmptyString "abcdc") == Just 2 --- | indexOf (Pattern "c") (NonEmptyString "aaa") == Nothing --- | ``` -indexOf :: Pattern -> NonEmptyString -> Maybe Int -indexOf = liftS <<< String.indexOf - --- | Returns the index of the first occurrence of the pattern in the --- | given string, starting at the specified index. Returns `Nothing` if there is --- | no match. --- | --- | ```purescript --- | indexOf' (Pattern "a") 2 (NonEmptyString "ababa") == Just 2 --- | indexOf' (Pattern "a") 3 (NonEmptyString "ababa") == Just 4 --- | ``` -indexOf' :: Pattern -> Int -> NonEmptyString -> Maybe Int -indexOf' pat = liftS <<< String.indexOf' pat - --- | Returns the index of the last occurrence of the pattern in the --- | given string. Returns `Nothing` if there is no match. --- | --- | ```purescript --- | lastIndexOf (Pattern "c") (NonEmptyString "abcdc") == Just 4 --- | lastIndexOf (Pattern "c") (NonEmptyString "aaa") == Nothing --- | ``` -lastIndexOf :: Pattern -> NonEmptyString -> Maybe Int -lastIndexOf = liftS <<< String.lastIndexOf - --- | Returns the index of the last occurrence of the pattern in the --- | given string, starting at the specified index --- | and searching backwards towards the beginning of the string. --- | Returns `Nothing` if there is no match. --- | --- | ```purescript --- | lastIndexOf' (Pattern "a") 1 (NonEmptyString "ababa") == Just 0 --- | lastIndexOf' (Pattern "a") 3 (NonEmptyString "ababa") == Just 2 --- | lastIndexOf' (Pattern "a") 4 (NonEmptyString "ababa") == Just 4 --- | ``` -lastIndexOf' :: Pattern -> Int -> NonEmptyString -> Maybe Int -lastIndexOf' pat = liftS <<< String.lastIndexOf' pat - --- | Returns the number of characters the string is composed of. --- | --- | ```purescript --- | length (NonEmptyString "Hello World") == 11 --- | ``` -length :: NonEmptyString -> Int -length (NonEmptyString s) = String.length s - --- | Compare two strings in a locale-aware fashion. This is in contrast to --- | the `Ord` instance on `String` which treats strings as arrays of code --- | units: --- | --- | ```purescript --- | NonEmptyString "Γ€" `localeCompare` NonEmptyString "b" == LT --- | NonEmptyString "Γ€" `compare` NonEmptyString "b" == GT --- | ``` -localeCompare :: NonEmptyString -> NonEmptyString -> Ordering -localeCompare (NonEmptyString a) (NonEmptyString b) = String.localeCompare a b - --- | Replaces the first occurence of the pattern with the replacement string. --- | --- | ```purescript --- | replace (Pattern "<=") (NonEmptyReplacement "≀") (NonEmptyString "a <= b <= c") == NonEmptyString "a ≀ b <= c" --- | ``` -replace :: Pattern -> NonEmptyReplacement -> NonEmptyString -> NonEmptyString -replace pat (NonEmptyReplacement (NonEmptyString rep)) (NonEmptyString s) = - NonEmptyString (String.replace pat (String.Replacement rep) s) - --- | Replaces all occurences of the pattern with the replacement string. --- | --- | ```purescript --- | replaceAll (Pattern "<=") (NonEmptyReplacement "≀") (NonEmptyString "a <= b <= c") == NonEmptyString "a ≀ b ≀ c" --- | ``` -replaceAll :: Pattern -> NonEmptyReplacement -> NonEmptyString -> NonEmptyString -replaceAll pat (NonEmptyReplacement (NonEmptyString rep)) (NonEmptyString s) = - NonEmptyString (String.replaceAll pat (String.Replacement rep) s) - --- | Returns the first `n` characters of the string. Returns `Nothing` if `n` is --- | less than 1. --- | --- | ```purescript --- | take 5 (NonEmptyString "Hello World") == Just (NonEmptyString "Hello") --- | take 0 (NonEmptyString "Hello World") == Nothing --- | ``` -take :: Int -> NonEmptyString -> Maybe NonEmptyString -take i (NonEmptyString s) - | i < 1 = Nothing - | otherwise = Just (NonEmptyString (String.take i s)) - --- | Returns the last `n` characters of the string. Returns `Nothing` if `n` is --- | less than 1. --- | --- | ```purescript --- | take 5 (NonEmptyString "Hello World") == Just (NonEmptyString "World") --- | take 0 (NonEmptyString "Hello World") == Nothing --- | ``` -takeRight :: Int -> NonEmptyString -> Maybe NonEmptyString -takeRight i (NonEmptyString s) - | i < 1 = Nothing - | otherwise = Just (NonEmptyString (String.takeRight i s)) - --- | Returns the string without the first `n` characters. Returns `Nothing` if --- | more characters are dropped than the string is long. --- | --- | ```purescript --- | drop 6 (NonEmptyString "Hello World") == Just (NonEmptyString "World") --- | drop 20 (NonEmptyString "Hello World") == Nothing --- | ``` -drop :: Int -> NonEmptyString -> Maybe NonEmptyString -drop i (NonEmptyString s) - | i >= String.length s = Nothing - | otherwise = Just (NonEmptyString (String.drop i s)) - --- | Returns the string without the last `n` characters. Returns `Nothing` if --- | more characters are dropped than the string is long. --- | --- | ```purescript --- | dropRight 6 (NonEmptyString "Hello World") == Just (NonEmptyString "Hello") --- | dropRight 20 (NonEmptyString "Hello World") == Nothing --- | ``` -dropRight :: Int -> NonEmptyString -> Maybe NonEmptyString -dropRight i (NonEmptyString s) - | i >= String.length s = Nothing - | otherwise = Just (NonEmptyString (String.dropRight i s)) - --- | Returns the number of contiguous characters at the beginning of the string --- | for which the predicate holds. --- | --- | ```purescript --- | countPrefix (_ /= 'o') (NonEmptyString "Hello World") == 4 --- | ``` -countPrefix :: (Char -> Boolean) -> NonEmptyString -> Int -countPrefix = liftS <<< String.countPrefix - --- | Returns the substrings of a split at the given index, if the index is --- | within bounds. --- | --- | ```purescript --- | splitAt 2 (NonEmptyString "Hello World") == Just { before: Just (NonEmptyString "He"), after: Just (NonEmptyString "llo World") } --- | splitAt 10 (NonEmptyString "Hi") == Nothing --- | ``` -splitAt - :: Int - -> NonEmptyString - -> { 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. --- | --- | ```purescript --- | toLower (NonEmptyString "hElLo") == NonEmptyString "hello" --- | ``` -toLower :: NonEmptyString -> NonEmptyString -toLower (NonEmptyString s) = NonEmptyString (String.toLower s) - --- | Returns the argument converted to uppercase. --- | --- | ```purescript --- | toUpper (NonEmptyString "Hello") == NonEmptyString "HELLO" --- | ``` -toUpper :: NonEmptyString -> NonEmptyString -toUpper (NonEmptyString s) = NonEmptyString (String.toUpper s) - --- | Removes whitespace from the beginning and end of a string, including --- | [whitespace characters](http://www.ecma-international.org/ecma-262/5.1/#sec-7.2) --- | and [line terminators](http://www.ecma-international.org/ecma-262/5.1/#sec-7.3). --- | If the string is entirely made up of whitespace the result will be Nothing. --- | --- | ```purescript --- | trim (NonEmptyString " Hello \n World\n\t ") == Just (NonEmptyString "Hello \n World") --- | trim (NonEmptyString " \n") == Nothing --- | ``` -trim :: NonEmptyString -> Maybe NonEmptyString -trim (NonEmptyString s) = fromString (String.trim s) - --- | Joins the strings in a container together as a new string, inserting the --- | first argument as separator between them. The result is not guaranteed to --- | be non-empty. --- | --- | ```purescript --- | joinWith ", " [NonEmptyString "apple", NonEmptyString "banana"] == "apple, banana" --- | joinWith ", " [] == "" --- | ``` -joinWith :: forall f. Foldable f => String -> f NonEmptyString -> String -joinWith splice = F.intercalate splice <<< coe - where - coe :: f NonEmptyString -> f String - coe = unsafeCoerce - --- | Joins non-empty strings in a non-empty container together as a new --- | non-empty string, inserting a possibly empty string as separator between --- | them. The result is guaranteed to be non-empty. --- | --- | ```purescript --- | -- array syntax is used for demonstration here, it would need to be a real `Foldable1` --- | join1With ", " [NonEmptyString "apple", NonEmptyString "banana"] == NonEmptyString "apple, banana" --- | join1With "" [NonEmptyString "apple", NonEmptyString "banana"] == NonEmptyString "applebanana" --- | ``` -join1With :: forall f. Foldable1 f => String -> f NonEmptyString -> NonEmptyString -join1With splice = NonEmptyString <<< joinWith splice - --- | Joins possibly empty strings in a non-empty container together as a new --- | non-empty string, inserting a non-empty string as a separator between them. --- | The result is guaranteed to be non-empty. --- | --- | ```purescript --- | -- array syntax is used for demonstration here, it would need to be a real `Foldable1` --- | joinWith1 (NonEmptyString ", ") ["apple", "banana"] == NonEmptyString "apple, banana" --- | joinWith1 (NonEmptyString "/") ["a", "b", "", "c", ""] == NonEmptyString "a/b//c/" --- | ``` -joinWith1 :: forall f. Foldable1 f => NonEmptyString -> f String -> NonEmptyString -joinWith1 (NonEmptyString splice) = NonEmptyString <<< F.intercalate splice - -liftS :: forall r. (String -> r) -> NonEmptyString -> r -liftS f (NonEmptyString s) = f s diff --git a/src/Data/String/Pattern.purs b/src/Data/String/Pattern.purs new file mode 100644 index 0000000..e0aea96 --- /dev/null +++ b/src/Data/String/Pattern.purs @@ -0,0 +1,33 @@ +module Data.String.Pattern where + +import Prelude + +import Data.Newtype (class Newtype) + +-- | A newtype used in cases where there is a string to be matched. +-- | +-- | ```purescript +-- | pursPattern = Pattern ".purs" +-- | --can be used like this: +-- | contains pursPattern "Test.purs" +-- | == true +-- | ``` +-- | +newtype Pattern = Pattern String + +derive instance eqPattern :: Eq Pattern +derive instance ordPattern :: Ord Pattern +derive instance newtypePattern :: Newtype Pattern _ + +instance showPattern :: Show Pattern where + show (Pattern s) = "(Pattern " <> show s <> ")" + +-- | A newtype used in cases to specify a replacement for a pattern. +newtype Replacement = Replacement String + +derive instance eqReplacement :: Eq Replacement +derive instance ordReplacement :: Ord Replacement +derive instance newtypeReplacement :: Newtype Replacement _ + +instance showReplacement :: Show Replacement where + show (Replacement s) = "(Replacement " <> show s <> ")" From d723c305bdb20fcbfaf2c2bf16adee958b3df0b6 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Mon, 21 May 2018 15:29:20 +0100 Subject: [PATCH 143/186] Major re-arrange for CodeUnit/CodePoint split --- bower.json | 7 +- src/Data/Char.js | 8 - src/Data/Char.purs | 10 +- src/Data/Char/Gen.purs | 14 +- src/Data/String.purs | 140 +--- ...sitiveString.purs => CaseInsensitive.purs} | 0 src/Data/String/CodePoint.purs | 446 ---------- src/Data/String/CodePoints.purs | 441 +++++----- src/Data/String/CodeUnits.js | 8 +- src/Data/String/CodeUnits.purs | 77 +- src/Data/{String.js => String/Common.js} | 0 src/Data/String/Common.purs | 96 +++ src/Data/String/NonEmpty.purs | 241 ++++++ src/Data/String/NonEmpty/CaseInsensitive.purs | 22 + src/Data/String/NonEmpty/CodePoints.purs | 136 +++ src/Data/String/NonEmpty/CodeUnits.purs | 298 +++++++ src/Data/String/Unsafe.js | 7 - src/Data/String/Unsafe.purs | 6 - test/Test/Data/Char.purs | 35 +- test/Test/Data/String.purs | 299 +++---- test/Test/Data/String/CaseInsensitive.purs | 18 +- test/Test/Data/String/CodePoints.purs | 791 ++++++++++++++---- test/Test/Data/String/CodeUnits.purs | 456 ++++++++++ test/Test/Data/String/NonEmpty.purs | 403 ++++----- test/Test/Data/String/NonEmpty/CodeUnits.purs | 450 ++++++++++ test/Test/Data/String/Unsafe.purs | 27 +- test/Test/Main.purs | 6 + 27 files changed, 2948 insertions(+), 1494 deletions(-) rename src/Data/String/{CaseInsensitiveString.purs => CaseInsensitive.purs} (100%) delete mode 100644 src/Data/String/CodePoint.purs rename src/Data/{String.js => String/Common.js} (100%) create mode 100644 src/Data/String/Common.purs create mode 100644 src/Data/String/NonEmpty.purs create mode 100644 src/Data/String/NonEmpty/CaseInsensitive.purs create mode 100644 src/Data/String/NonEmpty/CodePoints.purs create mode 100644 src/Data/String/NonEmpty/CodeUnits.purs create mode 100644 test/Test/Data/String/CodeUnits.purs create mode 100644 test/Test/Data/String/NonEmpty/CodeUnits.purs diff --git a/bower.json b/bower.json index 225c8bc..db0c9b5 100644 --- a/bower.json +++ b/bower.json @@ -17,13 +17,14 @@ "package.json" ], "dependencies": { + "purescript-arrays": "#compiler/0.12", "purescript-either": "#compiler/0.12", + "purescript-enums": "#compiler/0.12", "purescript-gen": "#compiler/0.12", + "purescript-integers": "#compiler/0.12", "purescript-maybe": "#compiler/0.12", "purescript-partial": "#compiler/0.12", - "purescript-unfoldable": "#compiler/0.12", - "purescript-arrays": "#compiler/0.12", - "purescript-integers": "#compiler/0.12" + "purescript-unfoldable": "#compiler/0.12" }, "devDependencies": { "purescript-assert": "#compiler/0.12", diff --git a/src/Data/Char.js b/src/Data/Char.js index 15f12b2..d396533 100644 --- a/src/Data/Char.js +++ b/src/Data/Char.js @@ -1,13 +1,5 @@ "use strict"; -exports.toCharCode = function (c) { - return c.charCodeAt(0); -}; - -exports.fromCharCode = function (c) { - return String.fromCharCode(c); -}; - exports.toLower = function (c) { return c.toLowerCase(); }; diff --git a/src/Data/Char.purs b/src/Data/Char.purs index ffad899..169abb0 100644 --- a/src/Data/Char.purs +++ b/src/Data/Char.purs @@ -1,17 +1,9 @@ -- | A type and functions for single characters. module Data.Char - ( fromCharCode - , toCharCode - , toLower + ( toLower , toUpper ) where --- | Returns the numeric Unicode value of the character. -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 toLower :: Char -> Char diff --git a/src/Data/Char/Gen.purs b/src/Data/Char/Gen.purs index 313cab3..838ff29 100644 --- a/src/Data/Char/Gen.purs +++ b/src/Data/Char/Gen.purs @@ -3,24 +3,24 @@ module Data.Char.Gen where import Prelude import Control.Monad.Gen (class MonadGen, chooseInt, oneOf) -import Data.Char as C +import Data.Enum (toEnumWithDefaults) import Data.NonEmpty ((:|)) -- | Generates a character of the Unicode basic multilingual plane. genUnicodeChar :: forall m. MonadGen m => m Char -genUnicodeChar = C.fromCharCode <$> chooseInt 0 65536 +genUnicodeChar = toEnumWithDefaults bottom top <$> chooseInt 0 65536 -- | Generates a character in the ASCII character set, excluding control codes. genAsciiChar :: forall m. MonadGen m => m Char -genAsciiChar = C.fromCharCode <$> chooseInt 32 127 +genAsciiChar = toEnumWithDefaults bottom top <$> chooseInt 32 127 -- | Generates a character in the ASCII character set. genAsciiChar' :: forall m. MonadGen m => m Char -genAsciiChar' = C.fromCharCode <$> chooseInt 0 127 +genAsciiChar' = toEnumWithDefaults bottom top <$> chooseInt 0 127 -- | Generates a character that is a numeric digit. genDigitChar :: forall m. MonadGen m => m Char -genDigitChar = C.fromCharCode <$> chooseInt 48 57 +genDigitChar = toEnumWithDefaults bottom top <$> chooseInt 48 57 -- | Generates a character from the basic latin alphabet. genAlpha :: forall m. MonadGen m => m Char @@ -28,8 +28,8 @@ genAlpha = oneOf (genAlphaLowercase :| [genAlphaUppercase]) -- | Generates a lowercase character from the basic latin alphabet. genAlphaLowercase :: forall m. MonadGen m => m Char -genAlphaLowercase = C.fromCharCode <$> chooseInt 97 122 +genAlphaLowercase = toEnumWithDefaults bottom top <$> chooseInt 97 122 -- | Generates an uppercase character from the basic latin alphabet. genAlphaUppercase :: forall m. MonadGen m => m Char -genAlphaUppercase = C.fromCharCode <$> chooseInt 65 90 +genAlphaUppercase = toEnumWithDefaults bottom top <$> chooseInt 65 90 diff --git a/src/Data/String.purs b/src/Data/String.purs index b6fbd0d..a382e6e 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -1,141 +1,9 @@ --- | Wraps the functions of Javascript's `String` object. --- | A String represents a sequence of characters. --- | For details of the underlying implementation, see [String Reference at MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String). module Data.String ( module Data.String.Pattern - , contains - , null - , localeCompare - , replace - , replaceAll - , stripPrefix - , stripSuffix - , split - , toLower - , toUpper - , trim - , joinWith + , module Data.String.Common + , module Data.String.CodeUnits ) where -import Prelude - -import Data.Maybe (Maybe(..), isJust) -import Data.String.CodeUnits as SCU import Data.String.Pattern (Pattern(..), Replacement(..)) - --- | Returns `true` if the given string is empty. --- | --- | ```purescript --- | null "" == true --- | null "Hi" == false --- | ``` -null :: String -> Boolean -null s = s == "" - --- | If the string starts with the given prefix, return the portion of the --- | string left after removing it, as a Just value. Otherwise, return Nothing. --- | --- | ```purescript --- | stripPrefix (Pattern "http:") "http://purescript.org" == Just "//purescript.org" --- | stripPrefix (Pattern "http:") "https://purescript.org" == Nothing --- | ``` -stripPrefix :: Pattern -> String -> Maybe String -stripPrefix prefix@(Pattern prefixS) str = - case SCU.indexOf prefix str of - Just 0 -> Just $ SCU.drop (SCU.length prefixS) str - _ -> Nothing - --- | If the string ends with the given suffix, return the portion of the --- | string left after removing it, as a `Just` value. Otherwise, return --- | `Nothing`. --- | --- | ```purescript --- | stripSuffix (Pattern ".exe") "psc.exe" == Just "psc" --- | stripSuffix (Pattern ".exe") "psc" == Nothing --- | ``` -stripSuffix :: Pattern -> String -> Maybe String -stripSuffix suffix@(Pattern suffixS) str = - case SCU.lastIndexOf suffix str of - Just x | x == SCU.length str - SCU.length suffixS -> Just $ SCU.take x str - _ -> Nothing - --- | Checks whether the pattern appears in the given string. --- | --- | ```purescript --- | contains (Pattern "needle") "haystack with needle" == true --- | contains (Pattern "needle") "haystack" == false --- | ``` -contains :: Pattern -> String -> Boolean -contains pat = isJust <<< SCU.indexOf pat - --- | Compare two strings in a locale-aware fashion. This is in contrast to --- | the `Ord` instance on `String` which treats strings as arrays of code --- | units: --- | --- | ```purescript --- | "Γ€" `localeCompare` "b" == LT --- | "Γ€" `compare` "b" == GT --- | ``` -localeCompare :: String -> String -> Ordering -localeCompare = _localeCompare LT EQ GT - -foreign import _localeCompare - :: Ordering - -> Ordering - -> Ordering - -> String - -> String - -> Ordering - --- | Replaces the first occurence of the pattern with the replacement string. --- | --- | ```purescript --- | replace (Pattern "<=") (Replacement "≀") "a <= b <= c" == "a ≀ b <= c" --- | ``` -foreign import replace :: Pattern -> Replacement -> String -> String - --- | Replaces all occurences of the pattern with the replacement string. --- | --- | ```purescript --- | replaceAll (Pattern "<=") (Replacement "≀") "a <= b <= c" == "a ≀ b ≀ c" --- | ``` -foreign import replaceAll :: Pattern -> Replacement -> String -> String - --- | Returns the substrings of the second string separated along occurences --- | of the first string. --- | --- | ```purescript --- | split (Pattern " ") "hello world" == ["hello", "world"] --- | ``` -foreign import split :: Pattern -> String -> Array String - --- | Returns the argument converted to lowercase. --- | --- | ```purescript --- | toLower "hElLo" == "hello" --- | ``` -foreign import toLower :: String -> String - --- | Returns the argument converted to uppercase. --- | --- | ```purescript --- | toUpper "Hello" == "HELLO" --- | ``` -foreign import toUpper :: String -> String - --- | Removes whitespace from the beginning and end of a string, including --- | [whitespace characters](http://www.ecma-international.org/ecma-262/5.1/#sec-7.2) --- | and [line terminators](http://www.ecma-international.org/ecma-262/5.1/#sec-7.3). --- | --- | ```purescript --- | trim " Hello \n World\n\t " == "Hello \n World" --- | ``` -foreign import trim :: String -> String - --- | Joins the strings in the array together, inserting the first argument --- | as separator between them. --- | --- | ```purescript --- | joinWith ", " ["apple", "banana", "orange"] == "apple, banana, orange" --- | ``` -foreign import joinWith :: String -> Array String -> String +import Data.String.Common (joinWith, localeCompare, null, replace, replaceAll, split, toLower, toUpper, trim) +import Data.String.CodeUnits (contains, stripPrefix, stripSuffix) diff --git a/src/Data/String/CaseInsensitiveString.purs b/src/Data/String/CaseInsensitive.purs similarity index 100% rename from src/Data/String/CaseInsensitiveString.purs rename to src/Data/String/CaseInsensitive.purs diff --git a/src/Data/String/CodePoint.purs b/src/Data/String/CodePoint.purs deleted file mode 100644 index b8cec10..0000000 --- a/src/Data/String/CodePoint.purs +++ /dev/null @@ -1,446 +0,0 @@ --- | These functions allow PureScript strings to be treated as if they were --- | sequences of Unicode code points instead of their true underlying --- | implementation (sequences of UTF-16 code units). For nearly all uses of --- | strings, these functions should be preferred over the ones in `Data.String`. -module Data.String.CodePoints - ( CodePoint - , codePointAt - , fromInt - , toInt - , fromChar - , toCodePointArray - , fromCodePointArray - , countPrefix - , drop - , dropWhile - , indexOf - , indexOf' - , lastIndexOf - , lastIndexOf' - , length - , singleton - , splitAt - , take - , takeWhile - , uncons - ) where - -import Prelude - -import Data.Array as Array -import Data.Char (toCharCode) -import Data.Char as Char -import Data.Int (hexadecimal, toStringAs) -import Data.Maybe (Maybe(..)) -import Data.String as String -import Data.String.Unsafe as Unsafe -import Data.Tuple (Tuple(..)) -import Data.Unfoldable (unfoldr) - --- | CodePoint is an Int bounded between 0 and 0x10FFFF, corresponding to --- | Unicode code points. -newtype CodePoint = CodePoint Int - -derive instance eqCodePoint :: Eq CodePoint -derive instance ordCodePoint :: Ord CodePoint - -instance showCodePoint :: Show CodePoint where - show (CodePoint i) = "(CodePoint 0x" <> String.toUpper (toStringAs hexadecimal i) <> ")" - --- I would prefer that this smart constructor not need to exist and instead --- CodePoint just implements Enum, but the Enum module already depends on this --- one. To avoid the circular dependency, we just expose these two functions. --- | --- | ```purescript --- | >>> it = fromInt 0x1D400 -- U+1D400 MATHEMATICAL BOLD CAPITAL A --- | Just (CodePoint 0x1D400) --- | --- | >>> map singleton it --- | Just "𝐀" --- | --- | >>> fromInt 0x110000 -- does not correspond to a Unicode code point --- | Nothing --- | ``` --- | -fromInt :: Int -> Maybe CodePoint -fromInt n | 0 <= n && n <= 0x10FFFF = Just (CodePoint n) -fromInt n = Nothing - --- | --- | ```purescript --- | >>> toInt (fromChar 'B') --- | 66 --- | --- | >>> boldA = fromInt 0x1D400 --- | >>> boldA --- | Just (CodePoint 0x1D400) --- | >>> map toInt boldA --- | Just 119808 -- is the same as 0x1D400 --- | ``` --- | -toInt :: CodePoint -> Int -toInt (CodePoint n) = n - --- | Creates a CodePoint from a given Char. --- | --- | ```purescript --- | >>> fromChar 'B' --- | CodePoint 0x42 -- represents 'B' --- | ``` --- | -fromChar :: Char -> CodePoint -fromChar = toCharCode >>> CodePoint - -unsurrogate :: Int -> Int -> CodePoint -unsurrogate lead trail = CodePoint ((lead - 0xD800) * 0x400 + (trail - 0xDC00) + 0x10000) - -isLead :: Int -> Boolean -isLead cu = 0xD800 <= cu && cu <= 0xDBFF - -isTrail :: Int -> Boolean -isTrail cu = 0xDC00 <= cu && cu <= 0xDFFF - -fromCharCode :: Int -> String -fromCharCode = String.singleton <<< Char.fromCharCode - --- WARN: this function expects the String parameter to be non-empty -unsafeCodePointAt0 :: String -> CodePoint -unsafeCodePointAt0 = _unsafeCodePointAt0 unsafeCodePointAt0Fallback - -foreign import _unsafeCodePointAt0 - :: (String -> CodePoint) - -> String - -> CodePoint - -unsafeCodePointAt0Fallback :: String -> CodePoint -unsafeCodePointAt0Fallback s = - let cu0 = Unsafe.charCodeAt 0 s in - let cu1 = Unsafe.charCodeAt 1 s in - if isLead cu0 && isTrail cu1 - then unsurrogate cu0 cu1 - else CodePoint cu0 - - --- | Returns the first code point of the string after dropping the given number --- | of code points from the beginning, if there is such a code point. Operates --- | in constant space and in time linear to the given index. --- | --- | ```purescript --- | >>> codePointAt 1 "𝐀𝐀𝐀𝐀" --- | Just (CodePoint 0x1D400) -- represents "𝐀" --- | -- compare to Data.String: --- | >>> charAt 1 "𝐀𝐀𝐀𝐀" --- | Just 'οΏ½' --- | ``` --- | -codePointAt :: Int -> String -> Maybe CodePoint -codePointAt n _ | n < 0 = Nothing -codePointAt 0 "" = Nothing -codePointAt 0 s = Just (unsafeCodePointAt0 s) -codePointAt n s = _codePointAt codePointAtFallback Just Nothing unsafeCodePointAt0 n s - -foreign import _codePointAt - :: (Int -> String -> Maybe CodePoint) - -> (forall a. a -> Maybe a) - -> (forall a. Maybe a) - -> (String -> CodePoint) - -> Int - -> String - -> Maybe CodePoint - -codePointAtFallback :: Int -> String -> Maybe CodePoint -codePointAtFallback n s = case uncons s of - Just { head, tail } -> if n == 0 then Just head else codePointAtFallback (n - 1) tail - _ -> Nothing - - --- | Returns the number of code points in the leading sequence of code points --- | which all match the given predicate. Operates in constant space and in --- | time linear to the length of the string. --- | --- | ```purescript --- | >>> countPrefix (\c -> toInt c == 0x1D400) "𝐀𝐀 b c 𝐀" --- | 2 --- | ``` --- | -countPrefix :: (CodePoint -> Boolean) -> String -> Int -countPrefix = _countPrefix countFallback unsafeCodePointAt0 - -foreign import _countPrefix - :: ((CodePoint -> Boolean) -> String -> Int) - -> (String -> CodePoint) - -> (CodePoint -> Boolean) - -> String - -> Int - -countFallback :: (CodePoint -> Boolean) -> String -> Int -countFallback p s = countTail p s 0 - -countTail :: (CodePoint -> Boolean) -> String -> Int -> Int -countTail p s accum = case uncons s of - Just { head, tail } -> if p head then countTail p tail (accum + 1) else accum - _ -> accum - - --- | Drops the given number of code points from the beginning of the string. If --- | the string does not have that many code points, returns the empty string. --- | Operates in constant space and in time linear to the given number. --- | --- | ```purescript --- | >>> drop 5 "𝐀𝐀 b c" --- | "c" --- | -- compared to Data.String: --- | >>> drop 5 "𝐀𝐀 b c" --- | "b c" -- because "𝐀" occupies 2 code units --- | ``` --- | -drop :: Int -> String -> String -drop n s = String.drop (String.length (take n s)) s - - --- | Drops the leading sequence of code points which all match the given --- | predicate from the string. Operates in constant space and in time linear --- | to the length of the string. --- | --- | ```purescript --- | >>> dropWhile (\c -> toInt c == 0x1D400) "𝐀𝐀 b c 𝐀" --- | " b c 𝐀" --- | ``` --- | -dropWhile :: (CodePoint -> Boolean) -> String -> String -dropWhile p s = drop (countPrefix p s) s - - --- | Creates a string from an array of code points. Operates in space and time --- | linear to the length of the array. --- | --- | ```purescript --- | >>> codePointArray = toCodePointArray "c 𝐀" --- | >>> codePointArray --- | [CodePoint 0x63, CodePoint 0x20, CodePoint 0x1D400] --- | >>> fromCodePointArray codePointArray --- | "c 𝐀" --- | ``` --- | -fromCodePointArray :: Array CodePoint -> String -fromCodePointArray = _fromCodePointArray singletonFallback - -foreign import _fromCodePointArray - :: (CodePoint -> String) - -> Array CodePoint - -> String - --- | Returns the number of code points preceding the first match of the given --- | pattern in the string. Returns Nothing when no matches are found. --- | --- | ```purescript --- | >>> indexOf (Pattern "𝐀") "b 𝐀𝐀 c 𝐀" --- | Just 2 --- | >>> indexOf (Pattern "o") "b 𝐀𝐀 c 𝐀" --- | Nothing --- | ``` --- | -indexOf :: String.Pattern -> String -> Maybe Int -indexOf p s = (\i -> length (String.take i s)) <$> String.indexOf p s - - --- | Returns the number of code points preceding the first match of the given --- | pattern in the string. Pattern matches preceding the given index will be --- | ignored. Returns Nothing when no matches are found. --- | --- | ```purescript --- | >>> indexOf' (Pattern "𝐀") 4 "b 𝐀𝐀 c 𝐀" --- | Just 7 --- | >>> indexOf' (Pattern "o") 4 "b 𝐀𝐀 c 𝐀" --- | Nothing --- | ``` --- | -indexOf' :: String.Pattern -> Int -> String -> Maybe Int -indexOf' p i s = - let s' = drop i s in - (\k -> i + length (String.take k s')) <$> String.indexOf p s' - - --- | Returns the number of code points preceding the last match of the given --- | pattern in the string. Returns Nothing when no matches are found. --- | --- | ```purescript --- | >>> lastIndexOf (Pattern "𝐀") "b 𝐀𝐀 c 𝐀" --- | Just 7 --- | >>> lastIndexOf (Pattern "o") "b 𝐀𝐀 c 𝐀" --- | Nothing --- | ``` --- | -lastIndexOf :: String.Pattern -> String -> Maybe Int -lastIndexOf p s = (\i -> length (String.take i s)) <$> String.lastIndexOf p s - - --- | Returns the number of code points preceding the first match of the given --- | pattern in the string. Pattern matches following the given index will be --- | ignored. Returns Nothing when no matches are found. --- | --- | ```purescript --- | >>> lastIndexOf' (Pattern "𝐀") 5 "b 𝐀𝐀 c 𝐀" --- | Just 3 --- | >>> lastIndexOf' (Pattern "o") 5 "b 𝐀𝐀 c 𝐀" --- | Nothing --- | ``` --- | -lastIndexOf' :: String.Pattern -> Int -> String -> Maybe Int -lastIndexOf' p i s = - let i' = String.length (take i s) in - (\k -> length (String.take k s)) <$> String.lastIndexOf' p i' s - - --- | Returns the number of code points in the string. Operates in constant --- | space and in time linear to the length of the string. --- | --- | ```purescript --- | >>> length "b 𝐀𝐀 c 𝐀" --- | 8 --- | -- compare to Data.String: --- | >>> length "b 𝐀𝐀 c 𝐀" --- | 11 --- | ``` --- | -length :: String -> Int -length = Array.length <<< toCodePointArray - - --- | Creates a string containing just the given code point. Operates in --- | constant space and time. --- | --- | ```purescript --- | >>> map singleton (fromInt 0x1D400) --- | Just "𝐀" --- | ``` --- | -singleton :: CodePoint -> String -singleton = _singleton singletonFallback - -foreign import _singleton - :: (CodePoint -> String) - -> CodePoint - -> String - -singletonFallback :: CodePoint -> String -singletonFallback (CodePoint cp) | cp <= 0xFFFF = fromCharCode cp -singletonFallback (CodePoint cp) = - let lead = ((cp - 0x10000) / 0x400) + 0xD800 in - let trail = (cp - 0x10000) `mod` 0x400 + 0xDC00 in - fromCharCode lead <> fromCharCode trail - - --- | 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 𝐀" } --- | ``` --- | --- | 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 - { 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 --- | points, returns the empty string. Operates in constant space and in time --- | linear to the given number. --- | --- | ```purescript --- | >>> take 3 "b 𝐀𝐀 c 𝐀" --- | "b 𝐀" --- | -- compare to Data.String: --- | >>> take 3 "b 𝐀𝐀 c 𝐀" --- | "b οΏ½" --- | ``` --- | -take :: Int -> String -> String -take = _take takeFallback - -foreign import _take :: (Int -> String -> String) -> Int -> String -> String - -takeFallback :: Int -> String -> String -takeFallback n _ | n < 1 = "" -takeFallback n s = case uncons s of - Just { head, tail } -> singleton head <> takeFallback (n - 1) tail - _ -> s - - --- | Returns a string containing the leading sequence of code points which all --- | match the given predicate from the string. Operates in constant space and --- | in time linear to the length of the string. --- | --- | ```purescript --- | >>> takeWhile (\c -> toInt c == 0x1D400) "𝐀𝐀 b c 𝐀" --- | "𝐀𝐀" --- | ``` --- | -takeWhile :: (CodePoint -> Boolean) -> String -> String -takeWhile p s = take (countPrefix p s) s - - --- | Creates an array of code points from a string. Operates in space and time --- | linear to the length of the string. --- | --- | ```purescript --- | >>> codePointArray = toCodePointArray "b 𝐀𝐀" --- | >>> codePointArray --- | [CodePoint 0x62, CodePoint 0x20, CodePoint 0x1D400, CodePoint 0x1D400] --- | >>> map singleton codePointArray --- | ["b", " ", "𝐀", "𝐀"] --- | ``` --- | -toCodePointArray :: String -> Array CodePoint -toCodePointArray = _toCodePointArray toCodePointArrayFallback unsafeCodePointAt0 - -foreign import _toCodePointArray - :: (String -> Array CodePoint) - -> (String -> CodePoint) - -> String - -> Array CodePoint - -toCodePointArrayFallback :: String -> Array CodePoint -toCodePointArrayFallback s = unfoldr unconsButWithTuple s - -unconsButWithTuple :: String -> Maybe (Tuple CodePoint String) -unconsButWithTuple s = (\{ head, tail } -> Tuple head tail) <$> uncons s - - --- | Returns a record with the first code point and the remaining code points --- | of the string. Returns Nothing if the string is empty. Operates in --- | constant space and time. --- | --- | ```purescript --- | >>> uncons "𝐀𝐀 c 𝐀" --- | Just { head: CodePoint 0x1D400, tail: "𝐀 c 𝐀" } --- | >>> uncons "" --- | Nothing --- | ``` --- | -uncons :: String -> Maybe { head :: CodePoint, tail :: String } -uncons s = case String.length s of - 0 -> Nothing - 1 -> Just { head: CodePoint (Unsafe.charCodeAt 0 s), tail: "" } - _ -> - let cu0 = Unsafe.charCodeAt 0 s in - let cu1 = Unsafe.charCodeAt 1 s in - if isLead cu0 && isTrail cu1 - then Just { head: unsurrogate cu0 cu1, tail: String.drop 2 s } - else Just { head: CodePoint cu0, tail: String.drop 1 s } diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index f6c512b..473731c 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -1,38 +1,42 @@ -- | These functions allow PureScript strings to be treated as if they were -- | sequences of Unicode code points instead of their true underlying -- | implementation (sequences of UTF-16 code units). For nearly all uses of --- | strings, these functions should be preferred over the ones in `Data.String`. +-- | strings, these functions should be preferred over the ones in +-- | `Data.String.CodeUnits`. module Data.String.CodePoints - ( CodePoint - , codePointAt - , codePointFromInt - , codePointToInt + ( module Data.String + , CodePoint , codePointFromChar - , toCodePointArray + , singleton , fromCodePointArray + , toCodePointArray + , codePointAt + , uncons + , length , countPrefix - , drop - , dropWhile , indexOf , indexOf' , lastIndexOf , lastIndexOf' - , length - , singleton - , splitAt , take + -- , takeRight , takeWhile - , uncons + , drop + -- , dropRight + , dropWhile + -- , slice + , splitAt ) where import Prelude import Data.Array as Array -import Data.Char (toCharCode) -import Data.Char as Char +import Data.Enum (class BoundedEnum, class Enum, Cardinality(..), defaultPred, defaultSucc, fromEnum, toEnum, toEnumWithDefaults) import Data.Int (hexadecimal, toStringAs) import Data.Maybe (Maybe(..)) import Data.String as String +import Data.String.CodeUnits as CU +import Data.String (Pattern(..), Replacement(..), contains, joinWith, localeCompare, null, replace, replaceAll, split, stripPrefix, stripSuffix, toLower, toUpper, trim) import Data.String.Unsafe as Unsafe import Data.Tuple (Tuple(..)) import Data.Unfoldable (unfoldr) @@ -47,79 +51,98 @@ derive instance ordCodePoint :: Ord CodePoint instance showCodePoint :: Show CodePoint where show (CodePoint i) = "(CodePoint 0x" <> String.toUpper (toStringAs hexadecimal i) <> ")" --- I would prefer that this smart constructor not need to exist and instead --- CodePoint just implements Enum, but the Enum module already depends on this --- one. To avoid the circular dependency, we just expose these two functions. +instance boundedCodePoint :: Bounded CodePoint where + bottom = CodePoint 0 + top = CodePoint 0x10FFFF + +instance enumCodePoint :: Enum CodePoint where + succ = defaultSucc toEnum fromEnum + pred = defaultPred toEnum fromEnum + +instance boundedEnumCodePoint :: BoundedEnum CodePoint where + cardinality = Cardinality (0x10FFFF + 1) + fromEnum (CodePoint n) = n + toEnum n + | n >= 0 && n <= 0x10FFFF = Just (CodePoint n) + | otherwise = Nothing + +-- | Creates a CodePoint from a given Char. -- | -- | ```purescript --- | >>> it = codePointFromInt 0x1D400 -- U+1D400 MATHEMATICAL BOLD CAPITAL A --- | Just (CodePoint 0x1D400) --- | --- | >>> map singleton it --- | Just "𝐀" --- | --- | >>> codePointFromInt 0x110000 -- does not correspond to a Unicode code point --- | Nothing +-- | >>> codePointFromChar 'B' +-- | CodePoint 0x42 -- represents 'B' -- | ``` -- | -codePointFromInt :: Int -> Maybe CodePoint -codePointFromInt n | 0 <= n && n <= 0x10FFFF = Just (CodePoint n) -codePointFromInt n = Nothing +codePointFromChar :: Char -> CodePoint +codePointFromChar = fromEnum >>> CodePoint +-- | Creates a string containing just the given code point. Operates in +-- | constant space and time. -- | -- | ```purescript --- | >>> codePointToInt (codePointFromChar 'B') --- | 66 --- | --- | >>> boldA = codePointFromInt 0x1D400 --- | >>> boldA --- | Just (CodePoint 0x1D400) --- | >>> map codePointToInt boldA --- | Just 119808 -- is the same as 0x1D400 +-- | >>> map singleton (codePointFromInt 0x1D400) +-- | Just "𝐀" -- | ``` -- | -codePointToInt :: CodePoint -> Int -codePointToInt (CodePoint n) = n +singleton :: CodePoint -> String +singleton = _singleton singletonFallback --- | Creates a CodePoint from a given Char. +foreign import _singleton + :: (CodePoint -> String) + -> CodePoint + -> String + +singletonFallback :: CodePoint -> String +singletonFallback (CodePoint cp) | cp <= 0xFFFF = fromCharCode cp +singletonFallback (CodePoint cp) = + let lead = ((cp - 0x10000) / 0x400) + 0xD800 in + let trail = (cp - 0x10000) `mod` 0x400 + 0xDC00 in + fromCharCode lead <> fromCharCode trail + +-- | Creates a string from an array of code points. Operates in space and time +-- | linear to the length of the array. -- | -- | ```purescript --- | >>> codePointFromChar 'B' --- | CodePoint 0x42 -- represents 'B' +-- | >>> codePointArray = toCodePointArray "c 𝐀" +-- | >>> codePointArray +-- | [CodePoint 0x63, CodePoint 0x20, CodePoint 0x1D400] +-- | >>> fromCodePointArray codePointArray +-- | "c 𝐀" -- | ``` -- | -codePointFromChar :: Char -> CodePoint -codePointFromChar = toCharCode >>> CodePoint - -unsurrogate :: Int -> Int -> CodePoint -unsurrogate lead trail = CodePoint ((lead - 0xD800) * 0x400 + (trail - 0xDC00) + 0x10000) - -isLead :: Int -> Boolean -isLead cu = 0xD800 <= cu && cu <= 0xDBFF - -isTrail :: Int -> Boolean -isTrail cu = 0xDC00 <= cu && cu <= 0xDFFF +fromCodePointArray :: Array CodePoint -> String +fromCodePointArray = _fromCodePointArray singletonFallback -fromCharCode :: Int -> String -fromCharCode = String.singleton <<< Char.fromCharCode +foreign import _fromCodePointArray + :: (CodePoint -> String) + -> Array CodePoint + -> String --- WARN: this function expects the String parameter to be non-empty -unsafeCodePointAt0 :: String -> CodePoint -unsafeCodePointAt0 = _unsafeCodePointAt0 unsafeCodePointAt0Fallback +-- | Creates an array of code points from a string. Operates in space and time +-- | linear to the length of the string. +-- | +-- | ```purescript +-- | >>> codePointArray = toCodePointArray "b 𝐀𝐀" +-- | >>> codePointArray +-- | [CodePoint 0x62, CodePoint 0x20, CodePoint 0x1D400, CodePoint 0x1D400] +-- | >>> map singleton codePointArray +-- | ["b", " ", "𝐀", "𝐀"] +-- | ``` +-- | +toCodePointArray :: String -> Array CodePoint +toCodePointArray = _toCodePointArray toCodePointArrayFallback unsafeCodePointAt0 -foreign import _unsafeCodePointAt0 - :: (String -> CodePoint) +foreign import _toCodePointArray + :: (String -> Array CodePoint) + -> (String -> CodePoint) -> String - -> CodePoint + -> Array CodePoint -unsafeCodePointAt0Fallback :: String -> CodePoint -unsafeCodePointAt0Fallback s = - let cu0 = Unsafe.charCodeAt 0 s in - let cu1 = Unsafe.charCodeAt 1 s in - if isLead cu0 && isTrail cu1 - then unsurrogate cu0 cu1 - else CodePoint cu0 +toCodePointArrayFallback :: String -> Array CodePoint +toCodePointArrayFallback s = unfoldr unconsButWithTuple s +unconsButWithTuple :: String -> Maybe (Tuple CodePoint String) +unconsButWithTuple s = (\{ head, tail } -> Tuple head tail) <$> uncons s -- | Returns the first code point of the string after dropping the given number -- | of code points from the beginning, if there is such a code point. Operates @@ -153,6 +176,43 @@ codePointAtFallback n s = case uncons s of Just { head, tail } -> if n == 0 then Just head else codePointAtFallback (n - 1) tail _ -> Nothing +-- | Returns a record with the first code point and the remaining code points +-- | of the string. Returns Nothing if the string is empty. Operates in +-- | constant space and time. +-- | +-- | ```purescript +-- | >>> uncons "𝐀𝐀 c 𝐀" +-- | Just { head: CodePoint 0x1D400, tail: "𝐀 c 𝐀" } +-- | >>> uncons "" +-- | Nothing +-- | ``` +-- | +uncons :: String -> Maybe { head :: CodePoint, tail :: String } +uncons s = case CU.length s of + 0 -> Nothing + 1 -> Just { head: CodePoint (fromEnum (Unsafe.charAt 0 s)), tail: "" } + _ -> + let + cu0 = fromEnum (Unsafe.charAt 0 s) + cu1 = fromEnum (Unsafe.charAt 1 s) + in + if isLead cu0 && isTrail cu1 + then Just { head: unsurrogate cu0 cu1, tail: CU.drop 2 s } + else Just { head: CodePoint cu0, tail: CU.drop 1 s } + +-- | Returns the number of code points in the string. Operates in constant +-- | space and in time linear to the length of the string. +-- | +-- | ```purescript +-- | >>> length "b 𝐀𝐀 c 𝐀" +-- | 8 +-- | -- compare to Data.String: +-- | >>> length "b 𝐀𝐀 c 𝐀" +-- | 11 +-- | ``` +-- | +length :: String -> Int +length = Array.length <<< toCodePointArray -- | Returns the number of code points in the leading sequence of code points -- | which all match the given predicate. Operates in constant space and in @@ -181,55 +241,6 @@ countTail p s accum = case uncons s of Just { head, tail } -> if p head then countTail p tail (accum + 1) else accum _ -> accum - --- | Drops the given number of code points from the beginning of the string. If --- | the string does not have that many code points, returns the empty string. --- | Operates in constant space and in time linear to the given number. --- | --- | ```purescript --- | >>> drop 5 "𝐀𝐀 b c" --- | "c" --- | -- compared to Data.String: --- | >>> drop 5 "𝐀𝐀 b c" --- | "b c" -- because "𝐀" occupies 2 code units --- | ``` --- | -drop :: Int -> String -> String -drop n s = String.drop (String.length (take n s)) s - - --- | Drops the leading sequence of code points which all match the given --- | predicate from the string. Operates in constant space and in time linear --- | to the length of the string. --- | --- | ```purescript --- | >>> dropWhile (\c -> codePointToInt c == 0x1D400) "𝐀𝐀 b c 𝐀" --- | " b c 𝐀" --- | ``` --- | -dropWhile :: (CodePoint -> Boolean) -> String -> String -dropWhile p s = drop (countPrefix p s) s - - --- | Creates a string from an array of code points. Operates in space and time --- | linear to the length of the array. --- | --- | ```purescript --- | >>> codePointArray = toCodePointArray "c 𝐀" --- | >>> codePointArray --- | [CodePoint 0x63, CodePoint 0x20, CodePoint 0x1D400] --- | >>> fromCodePointArray codePointArray --- | "c 𝐀" --- | ``` --- | -fromCodePointArray :: Array CodePoint -> String -fromCodePointArray = _fromCodePointArray singletonFallback - -foreign import _fromCodePointArray - :: (CodePoint -> String) - -> Array CodePoint - -> String - -- | Returns the number of code points preceding the first match of the given -- | pattern in the string. Returns Nothing when no matches are found. -- | @@ -240,9 +251,8 @@ foreign import _fromCodePointArray -- | Nothing -- | ``` -- | -indexOf :: String.Pattern -> String -> Maybe Int -indexOf p s = (\i -> length (String.take i s)) <$> String.indexOf p s - +indexOf :: Pattern -> String -> Maybe Int +indexOf p s = (\i -> length (CU.take i s)) <$> CU.indexOf p s -- | Returns the number of code points preceding the first match of the given -- | pattern in the string. Pattern matches preceding the given index will be @@ -255,11 +265,10 @@ indexOf p s = (\i -> length (String.take i s)) <$> String.indexOf p s -- | Nothing -- | ``` -- | -indexOf' :: String.Pattern -> Int -> String -> Maybe Int +indexOf' :: Pattern -> Int -> String -> Maybe Int indexOf' p i s = let s' = drop i s in - (\k -> i + length (String.take k s')) <$> String.indexOf p s' - + (\k -> i + length (CU.take k s')) <$> CU.indexOf p s' -- | Returns the number of code points preceding the last match of the given -- | pattern in the string. Returns Nothing when no matches are found. @@ -271,9 +280,8 @@ indexOf' p i s = -- | Nothing -- | ``` -- | -lastIndexOf :: String.Pattern -> String -> Maybe Int -lastIndexOf p s = (\i -> length (String.take i s)) <$> String.lastIndexOf p s - +lastIndexOf :: Pattern -> String -> Maybe Int +lastIndexOf p s = (\i -> length (CU.take i s)) <$> CU.lastIndexOf p s -- | Returns the number of code points preceding the first match of the given -- | pattern in the string. Pattern matches following the given index will be @@ -286,50 +294,73 @@ lastIndexOf p s = (\i -> length (String.take i s)) <$> String.lastIndexOf p s -- | Nothing -- | ``` -- | -lastIndexOf' :: String.Pattern -> Int -> String -> Maybe Int +lastIndexOf' :: Pattern -> Int -> String -> Maybe Int lastIndexOf' p i s = - let i' = String.length (take i s) in - (\k -> length (String.take k s)) <$> String.lastIndexOf' p i' s + let i' = CU.length (take i s) in + (\k -> length (CU.take k s)) <$> CU.lastIndexOf' p i' s - --- | Returns the number of code points in the string. Operates in constant --- | space and in time linear to the length of the string. +-- | 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 +-- | points, returns the empty string. Operates in constant space and in time +-- | linear to the given number. -- | -- | ```purescript --- | >>> length "b 𝐀𝐀 c 𝐀" --- | 8 +-- | >>> take 3 "b 𝐀𝐀 c 𝐀" +-- | "b 𝐀" -- | -- compare to Data.String: --- | >>> length "b 𝐀𝐀 c 𝐀" --- | 11 +-- | >>> take 3 "b 𝐀𝐀 c 𝐀" +-- | "b οΏ½" -- | ``` -- | -length :: String -> Int -length = Array.length <<< toCodePointArray +take :: Int -> String -> String +take = _take takeFallback +foreign import _take :: (Int -> String -> String) -> Int -> String -> String --- | Creates a string containing just the given code point. Operates in --- | constant space and time. +takeFallback :: Int -> String -> String +takeFallback n _ | n < 1 = "" +takeFallback n s = case uncons s of + Just { head, tail } -> singleton head <> takeFallback (n - 1) tail + _ -> s + +-- | Returns a string containing the leading sequence of code points which all +-- | match the given predicate from the string. Operates in constant space and +-- | in time linear to the length of the string. -- | -- | ```purescript --- | >>> map singleton (codePointFromInt 0x1D400) --- | Just "𝐀" +-- | >>> takeWhile (\c -> codePointToInt c == 0x1D400) "𝐀𝐀 b c 𝐀" +-- | "𝐀𝐀" -- | ``` -- | -singleton :: CodePoint -> String -singleton = _singleton singletonFallback - -foreign import _singleton - :: (CodePoint -> String) - -> CodePoint - -> String +takeWhile :: (CodePoint -> Boolean) -> String -> String +takeWhile p s = take (countPrefix p s) s -singletonFallback :: CodePoint -> String -singletonFallback (CodePoint cp) | cp <= 0xFFFF = fromCharCode cp -singletonFallback (CodePoint cp) = - let lead = ((cp - 0x10000) / 0x400) + 0xD800 in - let trail = (cp - 0x10000) `mod` 0x400 + 0xDC00 in - fromCharCode lead <> fromCharCode trail +-- | Drops the given number of code points from the beginning of the string. If +-- | the string does not have that many code points, returns the empty string. +-- | Operates in constant space and in time linear to the given number. +-- | +-- | ```purescript +-- | >>> drop 5 "𝐀𝐀 b c" +-- | "c" +-- | -- compared to Data.String: +-- | >>> drop 5 "𝐀𝐀 b c" +-- | "b c" -- because "𝐀" occupies 2 code units +-- | ``` +-- | +drop :: Int -> String -> String +drop n s = CU.drop (CU.length (take n s)) s +-- | Drops the leading sequence of code points which all match the given +-- | predicate from the string. Operates in constant space and in time linear +-- | to the length of the string. +-- | +-- | ```purescript +-- | >>> dropWhile (\c -> codePointToInt c == 0x1D400) "𝐀𝐀 b c 𝐀" +-- | " b c 𝐀" +-- | ``` +-- | +dropWhile :: (CodePoint -> Boolean) -> String -> String +dropWhile p s = drop (countPrefix p s) s -- | Splits a string into two substrings, where `before` contains the code -- | points up to (but not including) the given index, and `after` contains the @@ -355,92 +386,36 @@ splitAt i s = 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 + , after: CU.drop (CU.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 --- | points, returns the empty string. Operates in constant space and in time --- | linear to the given number. --- | --- | ```purescript --- | >>> take 3 "b 𝐀𝐀 c 𝐀" --- | "b 𝐀" --- | -- compare to Data.String: --- | >>> take 3 "b 𝐀𝐀 c 𝐀" --- | "b οΏ½" --- | ``` --- | -take :: Int -> String -> String -take = _take takeFallback - -foreign import _take :: (Int -> String -> String) -> Int -> String -> String - -takeFallback :: Int -> String -> String -takeFallback n _ | n < 1 = "" -takeFallback n s = case uncons s of - Just { head, tail } -> singleton head <> takeFallback (n - 1) tail - _ -> s +unsurrogate :: Int -> Int -> CodePoint +unsurrogate lead trail = CodePoint ((lead - 0xD800) * 0x400 + (trail - 0xDC00) + 0x10000) +isLead :: Int -> Boolean +isLead cu = 0xD800 <= cu && cu <= 0xDBFF --- | Returns a string containing the leading sequence of code points which all --- | match the given predicate from the string. Operates in constant space and --- | in time linear to the length of the string. --- | --- | ```purescript --- | >>> takeWhile (\c -> codePointToInt c == 0x1D400) "𝐀𝐀 b c 𝐀" --- | "𝐀𝐀" --- | ``` --- | -takeWhile :: (CodePoint -> Boolean) -> String -> String -takeWhile p s = take (countPrefix p s) s +isTrail :: Int -> Boolean +isTrail cu = 0xDC00 <= cu && cu <= 0xDFFF +fromCharCode :: Int -> String +fromCharCode = CU.singleton <<< toEnumWithDefaults bottom top --- | Creates an array of code points from a string. Operates in space and time --- | linear to the length of the string. --- | --- | ```purescript --- | >>> codePointArray = toCodePointArray "b 𝐀𝐀" --- | >>> codePointArray --- | [CodePoint 0x62, CodePoint 0x20, CodePoint 0x1D400, CodePoint 0x1D400] --- | >>> map singleton codePointArray --- | ["b", " ", "𝐀", "𝐀"] --- | ``` --- | -toCodePointArray :: String -> Array CodePoint -toCodePointArray = _toCodePointArray toCodePointArrayFallback unsafeCodePointAt0 +-- WARN: this function expects the String parameter to be non-empty +unsafeCodePointAt0 :: String -> CodePoint +unsafeCodePointAt0 = _unsafeCodePointAt0 unsafeCodePointAt0Fallback -foreign import _toCodePointArray - :: (String -> Array CodePoint) - -> (String -> CodePoint) +foreign import _unsafeCodePointAt0 + :: (String -> CodePoint) -> String - -> Array CodePoint - -toCodePointArrayFallback :: String -> Array CodePoint -toCodePointArrayFallback s = unfoldr unconsButWithTuple s - -unconsButWithTuple :: String -> Maybe (Tuple CodePoint String) -unconsButWithTuple s = (\{ head, tail } -> Tuple head tail) <$> uncons s - + -> CodePoint --- | Returns a record with the first code point and the remaining code points --- | of the string. Returns Nothing if the string is empty. Operates in --- | constant space and time. --- | --- | ```purescript --- | >>> uncons "𝐀𝐀 c 𝐀" --- | Just { head: CodePoint 0x1D400, tail: "𝐀 c 𝐀" } --- | >>> uncons "" --- | Nothing --- | ``` --- | -uncons :: String -> Maybe { head :: CodePoint, tail :: String } -uncons s = case String.length s of - 0 -> Nothing - 1 -> Just { head: CodePoint (Unsafe.charCodeAt 0 s), tail: "" } - _ -> - let cu0 = Unsafe.charCodeAt 0 s in - let cu1 = Unsafe.charCodeAt 1 s in +unsafeCodePointAt0Fallback :: String -> CodePoint +unsafeCodePointAt0Fallback s = + let + cu0 = fromEnum (Unsafe.charAt 0 s) + cu1 = fromEnum (Unsafe.charAt 1 s) + in if isLead cu0 && isTrail cu1 - then Just { head: unsurrogate cu0 cu1, tail: String.drop 2 s } - else Just { head: CodePoint cu0, tail: String.drop 1 s } + then unsurrogate cu0 cu1 + else CodePoint cu0 diff --git a/src/Data/String/CodeUnits.js b/src/Data/String/CodeUnits.js index 425bb47..6590986 100644 --- a/src/Data/String/CodeUnits.js +++ b/src/Data/String/CodeUnits.js @@ -4,6 +4,10 @@ exports.fromCharArray = function (a) { return a.join(""); }; +exports.toCharArray = function (s) { + return s.split(""); +}; + exports.singleton = function (c) { return c; }; @@ -26,10 +30,6 @@ exports._toChar = function (just) { }; }; -exports.toCharArray = function (s) { - return s.split(""); -}; - exports.length = function (s) { return s.length; }; diff --git a/src/Data/String/CodeUnits.purs b/src/Data/String/CodeUnits.purs index 3c5ac9c..b67a639 100644 --- a/src/Data/String/CodeUnits.purs +++ b/src/Data/String/CodeUnits.purs @@ -1,12 +1,14 @@ --- | Wraps the functions of Javascript's `String` object. --- | A String represents a sequence of characters. --- | For details of the underlying implementation, see [String Reference at MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String). module Data.String.CodeUnits - ( singleton + ( module Data.String.Pattern + , module Data.String.Common + , stripPrefix + , stripSuffix + , contains + , singleton , fromCharArray + , toCharArray , charAt , toChar - , toCharArray , uncons , length , countPrefix @@ -26,10 +28,57 @@ module Data.String.CodeUnits import Prelude -import Data.Maybe (Maybe(..)) -import Data.String.Pattern (Pattern) +import Data.Maybe (Maybe(..), isJust) +import Data.String.Common (joinWith, localeCompare, null, replace, replaceAll, split, toLower, toUpper, trim) +import Data.String.Pattern (Pattern(..), Replacement(..)) import Data.String.Unsafe as U +------------------------------------------------------------------------------- +-- `stripPrefix`, `stripSuffix`, and `contains` are CodeUnit/CodePoint agnostic +-- as they are based on patterns rather than lengths/indices, but they need to +-- be defined in here to avoid a circular module dependency +------------------------------------------------------------------------------- + +-- | If the string starts with the given prefix, return the portion of the +-- | string left after removing it, as a Just value. Otherwise, return Nothing. +-- | +-- | ```purescript +-- | stripPrefix (Pattern "http:") "http://purescript.org" == Just "//purescript.org" +-- | stripPrefix (Pattern "http:") "https://purescript.org" == Nothing +-- | ``` +stripPrefix :: Pattern -> String -> Maybe String +stripPrefix prefix@(Pattern prefixS) str = + case indexOf prefix str of + Just 0 -> Just $ drop (length prefixS) str + _ -> Nothing + +-- | If the string ends with the given suffix, return the portion of the +-- | string left after removing it, as a `Just` value. Otherwise, return +-- | `Nothing`. +-- | +-- | ```purescript +-- | stripSuffix (Pattern ".exe") "psc.exe" == Just "psc" +-- | stripSuffix (Pattern ".exe") "psc" == Nothing +-- | ``` +stripSuffix :: Pattern -> String -> Maybe String +stripSuffix suffix@(Pattern suffixS) str = + case lastIndexOf suffix str of + Just x | x == length str - length suffixS -> Just $ take x str + _ -> Nothing + +-- | Checks whether the pattern appears in the given string. +-- | +-- | ```purescript +-- | contains (Pattern "needle") "haystack with needle" == true +-- | contains (Pattern "needle") "haystack" == false +-- | ``` +contains :: Pattern -> String -> Boolean +contains pat = isJust <<< indexOf pat + +------------------------------------------------------------------------------- +-- all functions past this point are CodeUnit specific +------------------------------------------------------------------------------- + -- | Returns a string of length `1` containing the given character. -- | -- | ```purescript @@ -45,6 +94,13 @@ foreign import singleton :: Char -> String -- | ``` foreign import fromCharArray :: Array Char -> String +-- | Converts the string into an array of characters. +-- | +-- | ```purescript +-- | toCharArray "Hello☺\n" == ['H','e','l','l','o','☺','\n'] +-- | ``` +foreign import toCharArray :: String -> Array Char + -- | Returns the character at the given index, if the index is within bounds. -- | -- | ```purescript @@ -78,13 +134,6 @@ foreign import _toChar -> String -> Maybe Char --- | Converts the string into an array of characters. --- | --- | ```purescript --- | toCharArray "Hello☺\n" == ['H','e','l','l','o','☺','\n'] --- | ``` -foreign import toCharArray :: String -> Array Char - -- | Returns the first character and the rest of the string, -- | if the string is not empty. -- | diff --git a/src/Data/String.js b/src/Data/String/Common.js similarity index 100% rename from src/Data/String.js rename to src/Data/String/Common.js diff --git a/src/Data/String/Common.purs b/src/Data/String/Common.purs new file mode 100644 index 0000000..9e3132e --- /dev/null +++ b/src/Data/String/Common.purs @@ -0,0 +1,96 @@ +module Data.String.Common + ( null + , localeCompare + , replace + , replaceAll + , split + , toLower + , toUpper + , trim + , joinWith + ) where + +import Prelude + +import Data.String.Pattern (Pattern, Replacement) + +-- | Returns `true` if the given string is empty. +-- | +-- | ```purescript +-- | null "" == true +-- | null "Hi" == false +-- | ``` +null :: String -> Boolean +null s = s == "" + +-- | Compare two strings in a locale-aware fashion. This is in contrast to +-- | the `Ord` instance on `String` which treats strings as arrays of code +-- | units: +-- | +-- | ```purescript +-- | "Γ€" `localeCompare` "b" == LT +-- | "Γ€" `compare` "b" == GT +-- | ``` +localeCompare :: String -> String -> Ordering +localeCompare = _localeCompare LT EQ GT + +foreign import _localeCompare + :: Ordering + -> Ordering + -> Ordering + -> String + -> String + -> Ordering + +-- | Replaces the first occurence of the pattern with the replacement string. +-- | +-- | ```purescript +-- | replace (Pattern "<=") (Replacement "≀") "a <= b <= c" == "a ≀ b <= c" +-- | ``` +foreign import replace :: Pattern -> Replacement -> String -> String + +-- | Replaces all occurences of the pattern with the replacement string. +-- | +-- | ```purescript +-- | replaceAll (Pattern "<=") (Replacement "≀") "a <= b <= c" == "a ≀ b ≀ c" +-- | ``` +foreign import replaceAll :: Pattern -> Replacement -> String -> String + +-- | Returns the substrings of the second string separated along occurences +-- | of the first string. +-- | +-- | ```purescript +-- | split (Pattern " ") "hello world" == ["hello", "world"] +-- | ``` +foreign import split :: Pattern -> String -> Array String + +-- | Returns the argument converted to lowercase. +-- | +-- | ```purescript +-- | toLower "hElLo" == "hello" +-- | ``` +foreign import toLower :: String -> String + +-- | Returns the argument converted to uppercase. +-- | +-- | ```purescript +-- | toUpper "Hello" == "HELLO" +-- | ``` +foreign import toUpper :: String -> String + +-- | Removes whitespace from the beginning and end of a string, including +-- | [whitespace characters](http://www.ecma-international.org/ecma-262/5.1/#sec-7.2) +-- | and [line terminators](http://www.ecma-international.org/ecma-262/5.1/#sec-7.3). +-- | +-- | ```purescript +-- | trim " Hello \n World\n\t " == "Hello \n World" +-- | ``` +foreign import trim :: String -> String + +-- | Joins the strings in the array together, inserting the first argument +-- | as separator between them. +-- | +-- | ```purescript +-- | joinWith ", " ["apple", "banana", "orange"] == "apple, banana, orange" +-- | ``` +foreign import joinWith :: String -> Array String -> String diff --git a/src/Data/String/NonEmpty.purs b/src/Data/String/NonEmpty.purs new file mode 100644 index 0000000..02988a1 --- /dev/null +++ b/src/Data/String/NonEmpty.purs @@ -0,0 +1,241 @@ +module Data.String.NonEmpty + ( NonEmptyString + , class MakeNonEmpty, nes + , NonEmptyReplacement(..) + , fromString + , unsafeFromString + , toString + , appendString + , prependString + , contains + , localeCompare + , replace + , replaceAll + , stripPrefix + , stripSuffix + , toLower + , toUpper + , trim + , joinWith + , join1With + , joinWith1 + , module Data.String.Pattern + ) where + +import Prelude + +import Data.Foldable (class Foldable) +import Data.Foldable as F +import Data.Maybe (Maybe(..), fromJust) +import Data.Semigroup.Foldable (class Foldable1) +import Data.String as String +import Data.String.Pattern (Pattern(..)) +import Data.Symbol (class IsSymbol, SProxy, reflectSymbol) +import Prim.TypeError as TE +import Unsafe.Coerce (unsafeCoerce) + +-- | A string that is known not to be empty. +newtype NonEmptyString = NonEmptyString String + +derive newtype instance eqNonEmptyString ∷ Eq NonEmptyString +derive newtype instance ordNonEmptyString ∷ Ord NonEmptyString +derive newtype instance semigroupNonEmptyString ∷ Semigroup NonEmptyString + +instance showNonEmptyString :: Show NonEmptyString where + show (NonEmptyString s) = "(NonEmptyString.unsafeFromString " <> show s <> ")" + +-- | A helper class for defining non-empty string values at compile time. +-- | +-- | ``` purescript +-- | something :: NonEmptyString +-- | something = nes (SProxy :: SProxy "something") +-- | ``` +class MakeNonEmpty (s :: Symbol) where + nes :: SProxy s -> NonEmptyString + +instance makeNonEmptyBad :: TE.Fail (TE.Text "Cannot create an NonEmptyString from an empty Symbol") => MakeNonEmpty "" where + nes _ = NonEmptyString "" + +else instance nonEmptyNonEmpty :: IsSymbol s => MakeNonEmpty s where + nes p = NonEmptyString (reflectSymbol p) + +-- | A newtype used in cases to specify a non-empty replacement for a pattern. +newtype NonEmptyReplacement = NonEmptyReplacement NonEmptyString + +derive newtype instance eqNonEmptyReplacement :: Eq NonEmptyReplacement +derive newtype instance ordNonEmptyReplacement :: Ord NonEmptyReplacement +derive newtype instance semigroupNonEmptyReplacement ∷ Semigroup NonEmptyReplacement + +instance showNonEmptyReplacement :: Show NonEmptyReplacement where + show (NonEmptyReplacement s) = "(NonEmptyReplacement " <> show s <> ")" + +-- | Creates a `NonEmptyString` from a `String`, returning `Nothing` if the +-- | input is empty. +-- | +-- | ```purescript +-- | fromString "" = Nothing +-- | fromString "hello" = Just (NES.unsafeFromString "hello") +-- | ``` +fromString :: String -> Maybe NonEmptyString +fromString = case _ of + "" -> Nothing + s -> Just (NonEmptyString s) + +-- | A partial version of `fromString`. +unsafeFromString :: Partial => String -> NonEmptyString +unsafeFromString = fromJust <<< fromString + +-- | Converts a `NonEmptyString` back into a standard `String`. +toString :: NonEmptyString -> String +toString (NonEmptyString s) = s + +-- | Appends a string to this non-empty string. Since one of the strings is +-- | non-empty we know the result will be too. +-- | +-- | ```purescript +-- | appendString (NonEmptyString "Hello") " world" == NonEmptyString "Hello world" +-- | appendString (NonEmptyString "Hello") "" == NonEmptyString "Hello" +-- | ``` +appendString :: NonEmptyString -> String -> NonEmptyString +appendString (NonEmptyString s1) s2 = NonEmptyString (s1 <> s2) + +-- | Prepends a string to this non-empty string. Since one of the strings is +-- | non-empty we know the result will be too. +-- | +-- | ```purescript +-- | prependString "be" (NonEmptyString "fore") == NonEmptyString "before" +-- | prependString "" (NonEmptyString "fore") == NonEmptyString "fore" +-- | ``` +prependString :: String -> NonEmptyString -> NonEmptyString +prependString s1 (NonEmptyString s2) = NonEmptyString (s1 <> s2) + +-- | If the string starts with the given prefix, return the portion of the +-- | string left after removing it. If the prefix does not match or there is no +-- | remainder, the result will be `Nothing`. +-- | +-- | ```purescript +-- | stripPrefix (Pattern "http:") (NonEmptyString "http://purescript.org") == Just (NonEmptyString "//purescript.org") +-- | stripPrefix (Pattern "http:") (NonEmptyString "https://purescript.org") == Nothing +-- | stripPrefix (Pattern "Hello!") (NonEmptyString "Hello!") == Nothing +-- | ``` +stripPrefix :: Pattern -> NonEmptyString -> Maybe NonEmptyString +stripPrefix pat = fromString <=< liftS (String.stripPrefix pat) + +-- | If the string ends with the given suffix, return the portion of the +-- | string left after removing it. If the suffix does not match or there is no +-- | remainder, the result will be `Nothing`. +-- | +-- | ```purescript +-- | stripSuffix (Pattern ".exe") (NonEmptyString "purs.exe") == Just (NonEmptyString "purs") +-- | stripSuffix (Pattern ".exe") (NonEmptyString "purs") == Nothing +-- | stripSuffix (Pattern "Hello!") (NonEmptyString "Hello!") == Nothing +-- | ``` +stripSuffix :: Pattern -> NonEmptyString -> Maybe NonEmptyString +stripSuffix pat = fromString <=< liftS (String.stripSuffix pat) + +-- | Checks whether the pattern appears in the given string. +-- | +-- | ```purescript +-- | contains (Pattern "needle") (NonEmptyString "haystack with needle") == true +-- | contains (Pattern "needle") (NonEmptyString "haystack") == false +-- | ``` +contains :: Pattern -> NonEmptyString -> Boolean +contains = liftS <<< String.contains + +-- | Compare two strings in a locale-aware fashion. This is in contrast to +-- | the `Ord` instance on `String` which treats strings as arrays of code +-- | units: +-- | +-- | ```purescript +-- | NonEmptyString "Γ€" `localeCompare` NonEmptyString "b" == LT +-- | NonEmptyString "Γ€" `compare` NonEmptyString "b" == GT +-- | ``` +localeCompare :: NonEmptyString -> NonEmptyString -> Ordering +localeCompare (NonEmptyString a) (NonEmptyString b) = String.localeCompare a b + +-- | Replaces the first occurence of the pattern with the replacement string. +-- | +-- | ```purescript +-- | replace (Pattern "<=") (NonEmptyReplacement "≀") (NonEmptyString "a <= b <= c") == NonEmptyString "a ≀ b <= c" +-- | ``` +replace :: Pattern -> NonEmptyReplacement -> NonEmptyString -> NonEmptyString +replace pat (NonEmptyReplacement (NonEmptyString rep)) (NonEmptyString s) = + NonEmptyString (String.replace pat (String.Replacement rep) s) + +-- | Replaces all occurences of the pattern with the replacement string. +-- | +-- | ```purescript +-- | replaceAll (Pattern "<=") (NonEmptyReplacement "≀") (NonEmptyString "a <= b <= c") == NonEmptyString "a ≀ b ≀ c" +-- | ``` +replaceAll :: Pattern -> NonEmptyReplacement -> NonEmptyString -> NonEmptyString +replaceAll pat (NonEmptyReplacement (NonEmptyString rep)) (NonEmptyString s) = + NonEmptyString (String.replaceAll pat (String.Replacement rep) s) + +-- | Returns the argument converted to lowercase. +-- | +-- | ```purescript +-- | toLower (NonEmptyString "hElLo") == NonEmptyString "hello" +-- | ``` +toLower :: NonEmptyString -> NonEmptyString +toLower (NonEmptyString s) = NonEmptyString (String.toLower s) + +-- | Returns the argument converted to uppercase. +-- | +-- | ```purescript +-- | toUpper (NonEmptyString "Hello") == NonEmptyString "HELLO" +-- | ``` +toUpper :: NonEmptyString -> NonEmptyString +toUpper (NonEmptyString s) = NonEmptyString (String.toUpper s) + +-- | Removes whitespace from the beginning and end of a string, including +-- | [whitespace characters](http://www.ecma-international.org/ecma-262/5.1/#sec-7.2) +-- | and [line terminators](http://www.ecma-international.org/ecma-262/5.1/#sec-7.3). +-- | If the string is entirely made up of whitespace the result will be Nothing. +-- | +-- | ```purescript +-- | trim (NonEmptyString " Hello \n World\n\t ") == Just (NonEmptyString "Hello \n World") +-- | trim (NonEmptyString " \n") == Nothing +-- | ``` +trim :: NonEmptyString -> Maybe NonEmptyString +trim (NonEmptyString s) = fromString (String.trim s) + +-- | Joins the strings in a container together as a new string, inserting the +-- | first argument as separator between them. The result is not guaranteed to +-- | be non-empty. +-- | +-- | ```purescript +-- | joinWith ", " [NonEmptyString "apple", NonEmptyString "banana"] == "apple, banana" +-- | joinWith ", " [] == "" +-- | ``` +joinWith :: forall f. Foldable f => String -> f NonEmptyString -> String +joinWith splice = F.intercalate splice <<< coe + where + coe :: f NonEmptyString -> f String + coe = unsafeCoerce + +-- | Joins non-empty strings in a non-empty container together as a new +-- | non-empty string, inserting a possibly empty string as separator between +-- | them. The result is guaranteed to be non-empty. +-- | +-- | ```purescript +-- | -- array syntax is used for demonstration here, it would need to be a real `Foldable1` +-- | join1With ", " [NonEmptyString "apple", NonEmptyString "banana"] == NonEmptyString "apple, banana" +-- | join1With "" [NonEmptyString "apple", NonEmptyString "banana"] == NonEmptyString "applebanana" +-- | ``` +join1With :: forall f. Foldable1 f => String -> f NonEmptyString -> NonEmptyString +join1With splice = NonEmptyString <<< joinWith splice + +-- | Joins possibly empty strings in a non-empty container together as a new +-- | non-empty string, inserting a non-empty string as a separator between them. +-- | The result is guaranteed to be non-empty. +-- | +-- | ```purescript +-- | -- array syntax is used for demonstration here, it would need to be a real `Foldable1` +-- | joinWith1 (NonEmptyString ", ") ["apple", "banana"] == NonEmptyString "apple, banana" +-- | joinWith1 (NonEmptyString "/") ["a", "b", "", "c", ""] == NonEmptyString "a/b//c/" +-- | ``` +joinWith1 :: forall f. Foldable1 f => NonEmptyString -> f String -> NonEmptyString +joinWith1 (NonEmptyString splice) = NonEmptyString <<< F.intercalate splice + +liftS :: forall r. (String -> r) -> NonEmptyString -> r +liftS f (NonEmptyString s) = f s diff --git a/src/Data/String/NonEmpty/CaseInsensitive.purs b/src/Data/String/NonEmpty/CaseInsensitive.purs new file mode 100644 index 0000000..d1c1719 --- /dev/null +++ b/src/Data/String/NonEmpty/CaseInsensitive.purs @@ -0,0 +1,22 @@ +module Data.String.NonEmpty.CaseInsensitive where + +import Prelude + +import Data.Newtype (class Newtype) +import Data.String.NonEmpty (NonEmptyString, toLower) + +-- | A newtype for case insensitive string comparisons and ordering. +newtype CaseInsensitiveNonEmptyString = CaseInsensitiveNonEmptyString NonEmptyString + +instance eqCaseInsensitiveNonEmptyString :: Eq CaseInsensitiveNonEmptyString where + eq (CaseInsensitiveNonEmptyString s1) (CaseInsensitiveNonEmptyString s2) = + toLower s1 == toLower s2 + +instance ordCaseInsensitiveNonEmptyString :: Ord CaseInsensitiveNonEmptyString where + compare (CaseInsensitiveNonEmptyString s1) (CaseInsensitiveNonEmptyString s2) = + compare (toLower s1) (toLower s2) + +instance showCaseInsensitiveNonEmptyString :: Show CaseInsensitiveNonEmptyString where + show (CaseInsensitiveNonEmptyString s) = "(CaseInsensitiveNonEmptyString " <> show s <> ")" + +derive instance newtypeCaseInsensitiveNonEmptyString :: Newtype CaseInsensitiveNonEmptyString _ diff --git a/src/Data/String/NonEmpty/CodePoints.purs b/src/Data/String/NonEmpty/CodePoints.purs new file mode 100644 index 0000000..5ec1254 --- /dev/null +++ b/src/Data/String/NonEmpty/CodePoints.purs @@ -0,0 +1,136 @@ +module Data.String.NonEmpty.CodePoints + ( module Data.String.NonEmpty + , fromCodePointArray + , fromNonEmptyCodePointArray + , singleton + , cons + , snoc + , fromFoldable1 + , toCodePointArray + , toNonEmptyCodePointArray + , codePointAt + , indexOf + , indexOf' + , lastIndexOf + , lastIndexOf' + , uncons + , length + , take + -- takeRight + , takeWhile + , drop + -- dropRight + , dropWhile + , countPrefix + , splitAt + ) where + +import Prelude + +import Data.Array.NonEmpty (NonEmptyArray) +import Data.Array.NonEmpty as NEA +import Data.Maybe (Maybe(..), fromJust) +import Data.Semigroup.Foldable (class Foldable1) +import Data.Semigroup.Foldable as F1 +import Data.String.CodePoints (CodePoint) +import Data.String.CodePoints as CP +import Data.String.NonEmpty (class MakeNonEmpty, NonEmptyReplacement(..), NonEmptyString, Pattern(..), appendString, contains, fromString, join1With, joinWith, joinWith1, localeCompare, nes, prependString, replace, replaceAll, stripPrefix, stripSuffix, toLower, toString, toUpper, trim, unsafeFromString) +import Partial.Unsafe (unsafePartial) +import Unsafe.Coerce (unsafeCoerce) + +toNonEmptyString :: String -> NonEmptyString +toNonEmptyString = unsafeCoerce + +fromNonEmptyString :: NonEmptyString -> String +fromNonEmptyString = unsafeCoerce + +liftS :: forall r. (String -> r) -> NonEmptyString -> r +liftS = unsafeCoerce + +fromCodePointArray :: Array CodePoint -> Maybe NonEmptyString +fromCodePointArray = case _ of + [] -> Nothing + cs -> Just (toNonEmptyString (CP.fromCodePointArray cs)) + +fromNonEmptyCodePointArray :: NonEmptyArray CodePoint -> NonEmptyString +fromNonEmptyCodePointArray = unsafePartial fromJust <<< fromCodePointArray <<< NEA.toArray + +singleton :: CodePoint -> NonEmptyString +singleton = toNonEmptyString <<< CP.singleton + +cons :: CodePoint -> String -> NonEmptyString +cons c s = toNonEmptyString (CP.singleton c <> s) + +snoc :: CodePoint -> String -> NonEmptyString +snoc c s = toNonEmptyString (s <> CP.singleton c) + +fromFoldable1 :: forall f. Foldable1 f => f CodePoint -> NonEmptyString +fromFoldable1 = F1.foldMap1 singleton + +toCodePointArray :: NonEmptyString -> Array CodePoint +toCodePointArray = CP.toCodePointArray <<< fromNonEmptyString + +toNonEmptyCodePointArray :: NonEmptyString -> NonEmptyArray CodePoint +toNonEmptyCodePointArray = unsafePartial fromJust <<< NEA.fromArray <<< toCodePointArray + +codePointAt :: Int -> NonEmptyString -> Maybe CodePoint +codePointAt = liftS <<< CP.codePointAt + +indexOf :: Pattern -> NonEmptyString -> Maybe Int +indexOf = liftS <<< CP.indexOf + +indexOf' :: Pattern -> Int -> NonEmptyString -> Maybe Int +indexOf' pat = liftS <<< CP.indexOf' pat + +lastIndexOf :: Pattern -> NonEmptyString -> Maybe Int +lastIndexOf = liftS <<< CP.lastIndexOf + +lastIndexOf' :: Pattern -> Int -> NonEmptyString -> Maybe Int +lastIndexOf' pat = liftS <<< CP.lastIndexOf' pat + +uncons :: NonEmptyString -> { head :: CodePoint, tail :: Maybe NonEmptyString } +uncons nes = + let + s = fromNonEmptyString nes + in + { head: unsafePartial fromJust (CP.codePointAt 0 s) + , tail: fromString (CP.drop 1 s) + } + +length :: NonEmptyString -> Int +length = CP.length <<< fromNonEmptyString + +take :: Int -> NonEmptyString -> Maybe NonEmptyString +take i nes = + let + s = fromNonEmptyString nes + in + if i < 1 + then Nothing + else Just (toNonEmptyString (CP.take i s)) + +takeWhile :: (CodePoint -> Boolean) -> NonEmptyString -> Maybe NonEmptyString +takeWhile f = fromString <<< liftS (CP.takeWhile f) + +drop :: Int -> NonEmptyString -> Maybe NonEmptyString +drop i nes = + let + s = fromNonEmptyString nes + in + if i >= CP.length s + then Nothing + else Just (toNonEmptyString (CP.drop i s)) + +dropWhile :: (CodePoint -> Boolean) -> NonEmptyString -> Maybe NonEmptyString +dropWhile f = fromString <<< liftS (CP.dropWhile f) + +countPrefix :: (CodePoint -> Boolean) -> NonEmptyString -> Int +countPrefix = liftS <<< CP.countPrefix + +splitAt + :: Int + -> NonEmptyString + -> { before :: Maybe NonEmptyString, after :: Maybe NonEmptyString } +splitAt i nes = + case CP.splitAt i (fromNonEmptyString nes) of + { before, after } -> { before: fromString before, after: fromString after } diff --git a/src/Data/String/NonEmpty/CodeUnits.purs b/src/Data/String/NonEmpty/CodeUnits.purs new file mode 100644 index 0000000..0a826f0 --- /dev/null +++ b/src/Data/String/NonEmpty/CodeUnits.purs @@ -0,0 +1,298 @@ +module Data.String.NonEmpty.CodeUnits + ( module Data.String.NonEmpty + , fromCharArray + , fromNonEmptyCharArray + , singleton + , cons + , snoc + , fromFoldable1 + , toCharArray + , toNonEmptyCharArray + , charAt + , toChar + , indexOf + , indexOf' + , lastIndexOf + , lastIndexOf' + , uncons + , length + , take + , takeRight + , takeWhile + , drop + , dropRight + , dropWhile + , countPrefix + , splitAt + ) where + +import Prelude + +import Data.Array.NonEmpty (NonEmptyArray) +import Data.Array.NonEmpty as NEA +import Data.Maybe (Maybe(..), fromJust) +import Data.Semigroup.Foldable (class Foldable1) +import Data.Semigroup.Foldable as F1 +import Data.String.CodeUnits as CU +import Data.String.Unsafe as U +import Data.String.NonEmpty (class MakeNonEmpty, NonEmptyReplacement(..), NonEmptyString, Pattern(..), appendString, contains, fromString, join1With, joinWith, joinWith1, localeCompare, nes, prependString, replace, replaceAll, stripPrefix, stripSuffix, toLower, toString, toUpper, trim, unsafeFromString) +import Partial.Unsafe (unsafePartial) +import Unsafe.Coerce (unsafeCoerce) + +toNonEmptyString :: String -> NonEmptyString +toNonEmptyString = unsafeCoerce + +fromNonEmptyString :: NonEmptyString -> String +fromNonEmptyString = unsafeCoerce + +liftS :: forall r. (String -> r) -> NonEmptyString -> r +liftS = unsafeCoerce + +-- | Creates a `NonEmptyString` from a character array `String`, returning +-- | `Nothing` if the input is empty. +-- | +-- | ```purescript +-- | fromCharArray [] = Nothing +-- | fromCharArray ['a', 'b', 'c'] = Just (NonEmptyString "abc") +-- | ``` +fromCharArray :: Array Char -> Maybe NonEmptyString +fromCharArray = case _ of + [] -> Nothing + cs -> Just (toNonEmptyString (CU.fromCharArray cs)) + +fromNonEmptyCharArray :: NonEmptyArray Char -> NonEmptyString +fromNonEmptyCharArray = unsafePartial fromJust <<< fromCharArray <<< NEA.toArray + +-- | Creates a `NonEmptyString` from a character. +singleton :: Char -> NonEmptyString +singleton = toNonEmptyString <<< CU.singleton + +-- | Creates a `NonEmptyString` from a string by prepending a character. +-- | +-- | ```purescript +-- | cons 'a' "bc" = NonEmptyString "abc" +-- | cons 'a' "" = NonEmptyString "a" +-- | ``` +cons :: Char -> String -> NonEmptyString +cons c s = toNonEmptyString (CU.singleton c <> s) + +-- | Creates a `NonEmptyString` from a string by appending a character. +-- | +-- | ```purescript +-- | snoc 'c' "ab" = NonEmptyString "abc" +-- | snoc 'a' "" = NonEmptyString "a" +-- | ``` +snoc :: Char -> String -> NonEmptyString +snoc c s = toNonEmptyString (s <> CU.singleton c) + +-- | Creates a `NonEmptyString` from a `Foldable1` container carrying +-- | characters. +fromFoldable1 :: forall f. Foldable1 f => f Char -> NonEmptyString +fromFoldable1 = F1.fold1 <<< coe + where + coe ∷ f Char -> f NonEmptyString + coe = unsafeCoerce + +-- | Converts the `NonEmptyString` into an array of characters. +-- | +-- | ```purescript +-- | toCharArray (NonEmptyString "Hello☺\n") == ['H','e','l','l','o','☺','\n'] +-- | ``` +toCharArray :: NonEmptyString -> Array Char +toCharArray = CU.toCharArray <<< fromNonEmptyString + +-- | Converts the `NonEmptyString` into a non-empty array of characters. +toNonEmptyCharArray :: NonEmptyString -> NonEmptyArray Char +toNonEmptyCharArray = unsafePartial fromJust <<< NEA.fromArray <<< toCharArray + +-- | Returns the character at the given index, if the index is within bounds. +-- | +-- | ```purescript +-- | charAt 2 (NonEmptyString "Hello") == Just 'l' +-- | charAt 10 (NonEmptyString "Hello") == Nothing +-- | ``` +charAt :: Int -> NonEmptyString -> Maybe Char +charAt = liftS <<< CU.charAt + +-- | Converts the `NonEmptyString` to a character, if the length of the string +-- | is exactly `1`. +-- | +-- | ```purescript +-- | toChar "H" == Just 'H' +-- | toChar "Hi" == Nothing +-- | ``` +toChar :: NonEmptyString -> Maybe Char +toChar = CU.toChar <<< fromNonEmptyString + +-- | Returns the index of the first occurrence of the pattern in the +-- | given string. Returns `Nothing` if there is no match. +-- | +-- | ```purescript +-- | indexOf (Pattern "c") (NonEmptyString "abcdc") == Just 2 +-- | indexOf (Pattern "c") (NonEmptyString "aaa") == Nothing +-- | ``` +indexOf :: Pattern -> NonEmptyString -> Maybe Int +indexOf = liftS <<< CU.indexOf + +-- | Returns the index of the first occurrence of the pattern in the +-- | given string, starting at the specified index. Returns `Nothing` if there is +-- | no match. +-- | +-- | ```purescript +-- | indexOf' (Pattern "a") 2 (NonEmptyString "ababa") == Just 2 +-- | indexOf' (Pattern "a") 3 (NonEmptyString "ababa") == Just 4 +-- | ``` +indexOf' :: Pattern -> Int -> NonEmptyString -> Maybe Int +indexOf' pat = liftS <<< CU.indexOf' pat + +-- | Returns the index of the last occurrence of the pattern in the +-- | given string. Returns `Nothing` if there is no match. +-- | +-- | ```purescript +-- | lastIndexOf (Pattern "c") (NonEmptyString "abcdc") == Just 4 +-- | lastIndexOf (Pattern "c") (NonEmptyString "aaa") == Nothing +-- | ``` +lastIndexOf :: Pattern -> NonEmptyString -> Maybe Int +lastIndexOf = liftS <<< CU.lastIndexOf + +-- | Returns the index of the last occurrence of the pattern in the +-- | given string, starting at the specified index +-- | and searching backwards towards the beginning of the string. +-- | Returns `Nothing` if there is no match. +-- | +-- | ```purescript +-- | lastIndexOf' (Pattern "a") 1 (NonEmptyString "ababa") == Just 0 +-- | lastIndexOf' (Pattern "a") 3 (NonEmptyString "ababa") == Just 2 +-- | lastIndexOf' (Pattern "a") 4 (NonEmptyString "ababa") == Just 4 +-- | ``` +lastIndexOf' :: Pattern -> Int -> NonEmptyString -> Maybe Int +lastIndexOf' pat = liftS <<< CU.lastIndexOf' pat + +-- | Returns the first character and the rest of the string. +-- | +-- | ```purescript +-- | uncons "a" == { head: 'a', tail: Nothing } +-- | uncons "Hello World" == { head: 'H', tail: Just (NonEmptyString "ello World") } +-- | ``` +uncons :: NonEmptyString -> { head :: Char, tail :: Maybe NonEmptyString } +uncons nes = + let + s = fromNonEmptyString nes + in + { head: U.charAt 0 s + , tail: fromString (CU.drop 1 s) + } + +-- | Returns the number of characters the string is composed of. +-- | +-- | ```purescript +-- | length (NonEmptyString "Hello World") == 11 +-- | ``` +length :: NonEmptyString -> Int +length = CU.length <<< fromNonEmptyString + +-- | Returns the first `n` characters of the string. Returns `Nothing` if `n` is +-- | less than 1. +-- | +-- | ```purescript +-- | take 5 (NonEmptyString "Hello World") == Just (NonEmptyString "Hello") +-- | take 0 (NonEmptyString "Hello World") == Nothing +-- | ``` +take :: Int -> NonEmptyString -> Maybe NonEmptyString +take i nes = + let + s = fromNonEmptyString nes + in + if i < 1 + then Nothing + else Just (toNonEmptyString (CU.take i s)) + +-- | Returns the last `n` characters of the string. Returns `Nothing` if `n` is +-- | less than 1. +-- | +-- | ```purescript +-- | take 5 (NonEmptyString "Hello World") == Just (NonEmptyString "World") +-- | take 0 (NonEmptyString "Hello World") == Nothing +-- | ``` +takeRight :: Int -> NonEmptyString -> Maybe NonEmptyString +takeRight i nes = + let + s = fromNonEmptyString nes + in + if i < 1 + then Nothing + else Just (toNonEmptyString (CU.takeRight i s)) + +-- | Returns the longest prefix of characters that satisfy the predicate. +-- | `Nothing` is returned if there is no matching prefix. +-- | +-- | ```purescript +-- | takeWhile (_ /= ':') (NonEmptyString "http://purescript.org") == Just (NonEmptyString "http") +-- | takeWhile (_ == 'a') (NonEmptyString "xyz") == Nothing +-- | ``` +takeWhile :: (Char -> Boolean) -> NonEmptyString -> Maybe NonEmptyString +takeWhile f = fromString <<< liftS (CU.takeWhile f) + +-- | Returns the string without the first `n` characters. Returns `Nothing` if +-- | more characters are dropped than the string is long. +-- | +-- | ```purescript +-- | drop 6 (NonEmptyString "Hello World") == Just (NonEmptyString "World") +-- | drop 20 (NonEmptyString "Hello World") == Nothing +-- | ``` +drop :: Int -> NonEmptyString -> Maybe NonEmptyString +drop i nes = + let + s = fromNonEmptyString nes + in + if i >= CU.length s + then Nothing + else Just (toNonEmptyString (CU.drop i s)) + +-- | Returns the string without the last `n` characters. Returns `Nothing` if +-- | more characters are dropped than the string is long. +-- | +-- | ```purescript +-- | dropRight 6 (NonEmptyString "Hello World") == Just (NonEmptyString "Hello") +-- | dropRight 20 (NonEmptyString "Hello World") == Nothing +-- | ``` +dropRight :: Int -> NonEmptyString -> Maybe NonEmptyString +dropRight i nes = + let + s = fromNonEmptyString nes + in + if i >= CU.length s + then Nothing + else Just (toNonEmptyString (CU.dropRight i s)) + +-- | Returns the suffix remaining after `takeWhile`. +-- | +-- | ```purescript +-- | dropWhile (_ /= '.') (NonEmptyString "Test.purs") == Just (NonEmptyString ".purs") +-- | ``` +dropWhile :: (Char -> Boolean) -> NonEmptyString -> Maybe NonEmptyString +dropWhile f = fromString <<< liftS (CU.dropWhile f) + +-- | Returns the number of contiguous characters at the beginning of the string +-- | for which the predicate holds. +-- | +-- | ```purescript +-- | countPrefix (_ /= 'o') (NonEmptyString "Hello World") == 4 +-- | ``` +countPrefix :: (Char -> Boolean) -> NonEmptyString -> Int +countPrefix = liftS <<< CU.countPrefix + +-- | Returns the substrings of a split at the given index, if the index is +-- | within bounds. +-- | +-- | ```purescript +-- | splitAt 2 (NonEmptyString "Hello World") == Just { before: Just (NonEmptyString "He"), after: Just (NonEmptyString "llo World") } +-- | splitAt 10 (NonEmptyString "Hi") == Nothing +-- | ``` +splitAt + :: Int + -> NonEmptyString + -> { before :: Maybe NonEmptyString, after :: Maybe NonEmptyString } +splitAt i nes = + case CU.splitAt i (fromNonEmptyString nes) of + { before, after } -> { before: fromString before, after: fromString after } diff --git a/src/Data/String/Unsafe.js b/src/Data/String/Unsafe.js index b54568f..d7a17ca 100644 --- a/src/Data/String/Unsafe.js +++ b/src/Data/String/Unsafe.js @@ -1,12 +1,5 @@ "use strict"; -exports.charCodeAt = function (i) { - return function (s) { - if (i >= 0 && i < s.length) return s.charCodeAt(i); - throw new Error("Data.String.Unsafe.charCodeAt: Invalid index."); - }; -}; - exports.charAt = function (i) { return function (s) { if (i >= 0 && i < s.length) return s.charAt(i); diff --git a/src/Data/String/Unsafe.purs b/src/Data/String/Unsafe.purs index e9874f4..75f5037 100644 --- a/src/Data/String/Unsafe.purs +++ b/src/Data/String/Unsafe.purs @@ -2,14 +2,8 @@ module Data.String.Unsafe ( char , charAt - , charCodeAt ) where --- | Returns the numeric Unicode value of the character at the given index. --- | --- | **Unsafe:** throws runtime exception if the index is out of bounds. -foreign import charCodeAt :: Int -> String -> Int - -- | Returns the character at the given index. -- | -- | **Unsafe:** throws runtime exception if the index is out of bounds. diff --git a/test/Test/Data/Char.purs b/test/Test/Data/Char.purs index 869a2ca..f6d80ea 100644 --- a/test/Test/Data/Char.purs +++ b/test/Test/Data/Char.purs @@ -1,28 +1,31 @@ module Test.Data.Char (testChar) where -import Prelude (Unit, (==), ($), discard) +import Prelude +import Data.Char as C import Effect (Effect) import Effect.Console (log) - -import Data.Char - -import Test.Assert (assert) +import Test.Assert (assertEqual) testChar :: Effect Unit testChar = do - 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' + assertEqual + { actual: C.toLower 'A' + , expected: 'a' + } + assertEqual + { actual: C.toLower 'a' + , expected: 'a' + } log "toUpper" - assert $ toUpper 'a' == 'A' - assert $ toUpper 'A' == 'A' + assertEqual + { actual: C.toUpper 'a' + , expected: 'A' + } + assertEqual + { actual: C.toUpper 'A' + , expected: 'A' + } diff --git a/test/Test/Data/String.purs b/test/Test/Data/String.purs index 855ee5c..c496798 100644 --- a/test/Test/Data/String.purs +++ b/test/Test/Data/String.purs @@ -1,218 +1,141 @@ module Test.Data.String (testString) where -import Prelude (Unit, Ordering(..), (==), ($), discard, negate, not, (/=), (&&)) +import Prelude +import Data.Maybe (Maybe(..)) +import Data.String as S +import Data.String.Pattern (Pattern(..), Replacement(..)) import Effect (Effect) import Effect.Console (log) - -import Data.Maybe (Maybe(..), isNothing) -import Data.String - -import Test.Assert (assert) +import Test.Assert (assert, assertEqual) testString :: Effect Unit 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 "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" + assert $ S.null "" + assert $ not (S.null "a") log "stripPrefix" - assert $ stripPrefix (Pattern "") "" == Just "" - assert $ stripPrefix (Pattern "") "abc" == Just "abc" - assert $ stripPrefix (Pattern "a") "abc" == Just "bc" - assert $ stripPrefix (Pattern "!") "abc" == Nothing - assert $ stripPrefix (Pattern "!") "" == Nothing - - log "fromCharArray" - assert $ fromCharArray [] == "" - assert $ fromCharArray ['a', 'b'] == "ab" + assertEqual + { actual: S.stripPrefix (Pattern "") "" + , expected: Just "" + } + assertEqual + { actual: S.stripPrefix (Pattern "") "abc" + , expected: Just "abc" + } + assertEqual + { actual: S.stripPrefix (Pattern "a") "abc" + , expected: Just "bc" + } + assertEqual + { actual: S.stripPrefix (Pattern "!") "abc" + , expected: Nothing + } + assertEqual + { actual: S.stripPrefix (Pattern "!") "" + , expected: Nothing + } log "contains" - assert $ contains (Pattern "") "" - assert $ contains (Pattern "") "abcd" - assert $ contains (Pattern "bc") "abcd" - assert $ not (contains (Pattern "cb") "abcd") - - log "indexOf" - assert $ indexOf (Pattern "") "" == Just 0 - assert $ indexOf (Pattern "") "abcd" == Just 0 - assert $ indexOf (Pattern "bc") "abcd" == Just 1 - assert $ indexOf (Pattern "cb") "abcd" == Nothing - - log "indexOf'" - assert $ indexOf' (Pattern "") 0 "" == Just 0 - assert $ indexOf' (Pattern "") (-1) "ab" == Nothing - assert $ indexOf' (Pattern "") 0 "ab" == Just 0 - assert $ indexOf' (Pattern "") 1 "ab" == Just 1 - assert $ indexOf' (Pattern "") 2 "ab" == Just 2 - assert $ indexOf' (Pattern "") 3 "ab" == Nothing - assert $ indexOf' (Pattern "bc") 0 "abcd" == Just 1 - assert $ indexOf' (Pattern "bc") 1 "abcd" == Just 1 - assert $ indexOf' (Pattern "bc") 2 "abcd" == Nothing - assert $ indexOf' (Pattern "cb") 0 "abcd" == Nothing - - log "lastIndexOf" - assert $ lastIndexOf (Pattern "") "" == Just 0 - assert $ lastIndexOf (Pattern "") "abcd" == Just 4 - assert $ lastIndexOf (Pattern "bc") "abcd" == Just 1 - assert $ lastIndexOf (Pattern "cb") "abcd" == Nothing - - log "lastIndexOf'" - assert $ lastIndexOf' (Pattern "") 0 "" == Just 0 - assert $ lastIndexOf' (Pattern "") (-1) "ab" == Nothing - assert $ lastIndexOf' (Pattern "") 0 "ab" == Just 0 - assert $ lastIndexOf' (Pattern "") 1 "ab" == Just 1 - assert $ lastIndexOf' (Pattern "") 2 "ab" == Just 2 - assert $ lastIndexOf' (Pattern "") 3 "ab" == Nothing - assert $ lastIndexOf' (Pattern "bc") 0 "abcd" == Nothing - assert $ lastIndexOf' (Pattern "bc") 1 "abcd" == Just 1 - assert $ lastIndexOf' (Pattern "bc") 2 "abcd" == Just 1 - assert $ lastIndexOf' (Pattern "cb") 0 "abcd" == Nothing - - log "length" - assert $ length "" == 0 - assert $ length "a" == 1 - assert $ length "ab" == 2 + assert $ S.contains (Pattern "") "" + assert $ S.contains (Pattern "") "abcd" + assert $ S.contains (Pattern "bc") "abcd" + assert $ not S.contains (Pattern "cb") "abcd" log "localeCompare" - assert $ localeCompare "" "" == EQ - assert $ localeCompare "a" "a" == EQ - assert $ localeCompare "a" "b" == LT - assert $ localeCompare "b" "a" == GT + assertEqual + { actual: S.localeCompare "" "" + , expected: EQ + } + assertEqual + { actual: S.localeCompare "a" "a" + , expected: EQ + } + assertEqual + { actual: S.localeCompare "a" "b" + , expected: LT + } + assertEqual + { actual: S.localeCompare "b" "a" + , expected: GT + } log "replace" - assert $ replace (Pattern "b") (Replacement "") "abc" == "ac" - assert $ replace (Pattern "b") (Replacement "!") "abc" == "a!c" - assert $ replace (Pattern "d") (Replacement "!") "abc" == "abc" + assertEqual + { actual: S.replace (Pattern "b") (Replacement "") "abc" + , expected: "ac" + } + assertEqual + { actual: S.replace (Pattern "b") (Replacement "!") "abc" + , expected: "a!c" + } + assertEqual + { actual: S.replace (Pattern "d") (Replacement "!") "abc" + , expected: "abc" + } log "replaceAll" - assert $ replaceAll (Pattern "b") (Replacement "") "abbbbbc" == "ac" - assert $ replaceAll (Pattern "[b]") (Replacement "!") "a[b]c" == "a!c" - - log "take" - assert $ take 0 "ab" == "" - assert $ take 1 "ab" == "a" - assert $ take 2 "ab" == "ab" - assert $ take 3 "ab" == "ab" - assert $ take (-1) "ab" == "" - - log "takeRight" - assert $ takeRight 0 "ab" == "" - assert $ takeRight 1 "ab" == "b" - assert $ takeRight 2 "ab" == "ab" - assert $ takeRight 3 "ab" == "ab" - assert $ takeRight (-1) "ab" == "" - - log "drop" - assert $ drop 0 "ab" == "ab" - assert $ drop 1 "ab" == "b" - assert $ drop 2 "ab" == "" - assert $ drop 3 "ab" == "" - assert $ drop (-1) "ab" == "ab" - - log "dropRight" - assert $ dropRight 0 "ab" == "ab" - assert $ dropRight 1 "ab" == "a" - assert $ dropRight 2 "ab" == "" - assert $ dropRight 3 "ab" == "" - assert $ dropRight (-1) "ab" == "ab" - - log "countPrefix" - assert $ countPrefix (_ == 'a') "" == 0 - assert $ countPrefix (_ == 'a') "ab" == 1 - assert $ countPrefix (_ == 'a') "aaab" == 3 - assert $ countPrefix (_ == 'a') "abaa" == 1 + assertEqual + { actual: S.replaceAll (Pattern "b") (Replacement "") "abbbbbc" + , expected: "ac" + } + assertEqual + { actual: S.replaceAll (Pattern "[b]") (Replacement "!") "a[b]c" + , expected: "a!c" + } log "split" - assert $ split (Pattern "") "" == [] - assert $ split (Pattern "") "a" == ["a"] - assert $ split (Pattern "") "ab" == ["a", "b"] - assert $ split (Pattern "b") "aabcc" == ["aa", "cc"] - assert $ split (Pattern "d") "abc" == ["abc"] - - log "splitAt" - let testSplitAt i str r = - assert $ case splitAt i str of - { 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 "" == [] - assert $ toCharArray "a" == ['a'] - assert $ toCharArray "ab" == ['a', 'b'] + assertEqual + { actual: S.split (Pattern "") "" + , expected: [] + } + assertEqual + { actual: S.split (Pattern "") "a" + , expected: ["a"] + } + assertEqual + { actual: S.split (Pattern "") "ab" + , expected: ["a", "b"] + } + assertEqual + { actual: S.split (Pattern "b") "aabcc" + , expected: ["aa", "cc"] + } + assertEqual + { actual: S.split (Pattern "d") "abc" + , expected: ["abc"] + } log "toLower" - assert $ toLower "bAtMaN" == "batman" + assertEqual + { actual: S.toLower "bAtMaN" + , expected: "batman" + } log "toUpper" - assert $ toUpper "bAtMaN" == "BATMAN" + assertEqual + { actual: S.toUpper "bAtMaN" + , expected: "BATMAN" + } log "trim" - assert $ trim " abc " == "abc" + assertEqual + { actual: S.trim " abc " + , expected: "abc" + } log "joinWith" - assert $ joinWith "" [] == "" - assert $ joinWith "" ["a", "b"] == "ab" - assert $ joinWith "--" ["a", "b", "c"] == "a--b--c" - - log "slice" - assert $ slice 0 0 "purescript" == Just "" - assert $ slice 0 1 "purescript" == Just "p" - assert $ slice 3 6 "purescript" == Just "esc" - assert $ slice (-4) (-1) "purescript" == Just "rip" - assert $ slice (-4) 3 "purescript" == Nothing -- b' > e' - assert $ slice 1000 3 "purescript" == Nothing -- b' > e' (subsumes b > l) - assert $ slice 2 (-15) "purescript" == Nothing -- e' < 0 - assert $ slice (-15) 9 "purescript" == Nothing -- b' < 0 - assert $ slice 3 1000 "purescript" == Nothing -- e > l + assertEqual + { actual: S.joinWith "" [] + , expected: "" + } + assertEqual + { actual: S.joinWith "" ["a", "b"] + , expected: "ab" + } + assertEqual + { actual: S.joinWith "--" ["a", "b", "c"] + , expected: "a--b--c" + } diff --git a/test/Test/Data/String/CaseInsensitive.purs b/test/Test/Data/String/CaseInsensitive.purs index ec7d4bf..a263732 100644 --- a/test/Test/Data/String/CaseInsensitive.purs +++ b/test/Test/Data/String/CaseInsensitive.purs @@ -1,18 +1,22 @@ module Test.Data.String.CaseInsensitive (testCaseInsensitiveString) where -import Prelude (Unit, (==), ($), discard, compare, Ordering(..)) +import Prelude +import Data.String.CaseInsensitive (CaseInsensitiveString(..)) import Effect (Effect) import Effect.Console (log) - -import Data.String.CaseInsensitive - -import Test.Assert (assert) +import Test.Assert (assertEqual) testCaseInsensitiveString :: Effect Unit testCaseInsensitiveString = do log "equality" - assert $ CaseInsensitiveString "aB" == CaseInsensitiveString "AB" + assertEqual + { actual: CaseInsensitiveString "aB" + , expected: CaseInsensitiveString "AB" + } log "comparison" - assert $ compare (CaseInsensitiveString "qwerty") (CaseInsensitiveString "QWERTY") == EQ + assertEqual + { actual: compare (CaseInsensitiveString "qwerty") (CaseInsensitiveString "QWERTY") + , expected: EQ + } diff --git a/test/Test/Data/String/CodePoints.purs b/test/Test/Data/String/CodePoints.purs index 29c282d..a3e49b9 100644 --- a/test/Test/Data/String/CodePoints.purs +++ b/test/Test/Data/String/CodePoints.purs @@ -2,209 +2,644 @@ module Test.Data.String.CodePoints (testStringCodePoints) where import Prelude +import Data.Enum (fromEnum, toEnum) +import Data.Maybe (Maybe(..), fromJust) +import Data.String.CodePoints as SCP +import Data.String.Pattern (Pattern(..)) import Effect (Effect) import Effect.Console (log) - -import Data.Char (fromCharCode) -import Data.Maybe (Maybe(..), isNothing, maybe) -import Data.String.CodePoints - -import Test.Assert (assert) +import Partial.Unsafe (unsafePartial) +import Test.Assert (assertEqual) str :: String str = "a\xDC00\xD800\xD800\x16805\x16A06\&z" testStringCodePoints :: Effect Unit testStringCodePoints = do - log "show" - assert $ map show (codePointAt 0 str) == Just "(CodePoint 0x61)" - assert $ map show (codePointAt 1 str) == Just "(CodePoint 0xDC00)" - assert $ map show (codePointAt 2 str) == Just "(CodePoint 0xD800)" - assert $ map show (codePointAt 3 str) == Just "(CodePoint 0xD800)" - assert $ map show (codePointAt 4 str) == Just "(CodePoint 0x16805)" - assert $ map show (codePointAt 5 str) == Just "(CodePoint 0x16A06)" - assert $ map show (codePointAt 6 str) == Just "(CodePoint 0x7A)" - log "codePointAt" - assert $ codePointAt (-1) str == Nothing - assert $ codePointAt 0 str == (codePointFromInt 0x61) - assert $ codePointAt 1 str == (codePointFromInt 0xDC00) - assert $ codePointAt 2 str == (codePointFromInt 0xD800) - assert $ codePointAt 3 str == (codePointFromInt 0xD800) - assert $ codePointAt 4 str == (codePointFromInt 0x16805) - assert $ codePointAt 5 str == (codePointFromInt 0x16A06) - assert $ codePointAt 6 str == (codePointFromInt 0x7A) - assert $ codePointAt 7 str == Nothing + log "show" + assertEqual + { actual: map show (SCP.codePointAt 0 str) + , expected: Just "(CodePoint 0x61)" + } + assertEqual + { actual: map show (SCP.codePointAt 1 str) + , expected: Just "(CodePoint 0xDC00)" + } + assertEqual + { actual: map show (SCP.codePointAt 2 str) + , expected: Just "(CodePoint 0xD800)" + } + assertEqual + { actual: map show (SCP.codePointAt 3 str) + , expected: Just "(CodePoint 0xD800)" + } + assertEqual + { actual: map show (SCP.codePointAt 4 str) + , expected: Just "(CodePoint 0x16805)" + } + assertEqual + { actual: map show (SCP.codePointAt 5 str) + , expected: Just "(CodePoint 0x16A06)" + } + assertEqual + { actual: map show (SCP.codePointAt 6 str) + , expected: Just "(CodePoint 0x7A)" + } log "codePointFromChar" - assert $ Just (codePointFromChar 'A') == (codePointFromInt 65) - assert $ Just (codePointFromChar $ fromCharCode 0) == codePointFromInt 0 - assert $ Just (codePointFromChar $ fromCharCode 0xFFFF) == codePointFromInt 0xFFFF + assertEqual + { actual: Just (SCP.codePointFromChar 'A') + , expected: (toEnum 65) + } + assertEqual + { actual: (SCP.codePointFromChar <$> toEnum 0) + , expected: toEnum 0 + } + assertEqual + { actual: (SCP.codePointFromChar <$> toEnum 0xFFFF) + , expected: toEnum 0xFFFF + } - log "countPrefix" - assert $ countPrefix (\_ -> true) "" == 0 - assert $ countPrefix (\_ -> false) str == 0 - assert $ countPrefix (\_ -> true) str == 7 - assert $ countPrefix (\x -> codePointToInt x < 0xFFFF) str == 4 - assert $ countPrefix (\x -> codePointToInt x < 0xDC00) str == 1 + log "singleton" + assertEqual + { actual: (SCP.singleton <$> toEnum 0x30) + , expected: Just "0" + } + assertEqual + { actual: (SCP.singleton <$> toEnum 0x16805) + , expected: Just "\x16805" + } - log "drop" - assert $ drop (-1) str == str - assert $ drop 0 str == str - assert $ drop 1 str == "\xDC00\xD800\xD800\x16805\x16A06\&z" - assert $ drop 2 str == "\xD800\xD800\x16805\x16A06\&z" - assert $ drop 3 str == "\xD800\x16805\x16A06\&z" - assert $ drop 4 str == "\x16805\x16A06\&z" - assert $ drop 5 str == "\x16A06\&z" - assert $ drop 6 str == "z" - assert $ drop 7 str == "" - assert $ drop 8 str == "" + log "codePointAt" + assertEqual + { actual: SCP.codePointAt (-1) str + , expected: Nothing + } + assertEqual + { actual: SCP.codePointAt 0 str + , expected: (toEnum 0x61) + } + assertEqual + { actual: SCP.codePointAt 1 str + , expected: (toEnum 0xDC00) + } + assertEqual + { actual: SCP.codePointAt 2 str + , expected: (toEnum 0xD800) + } + assertEqual + { actual: SCP.codePointAt 3 str + , expected: (toEnum 0xD800) + } + assertEqual + { actual: SCP.codePointAt 4 str + , expected: (toEnum 0x16805) + } + assertEqual + { actual: SCP.codePointAt 5 str + , expected: (toEnum 0x16A06) + } + assertEqual + { actual: SCP.codePointAt 6 str + , expected: (toEnum 0x7A) + } + assertEqual + { actual: SCP.codePointAt 7 str + , expected: Nothing + } - log "dropWhile" - assert $ dropWhile (\_ -> true) str == "" - assert $ dropWhile (\_ -> false) str == str - assert $ dropWhile (\c -> codePointToInt c < 0xFFFF) str == "\x16805\x16A06\&z" - assert $ dropWhile (\c -> codePointToInt c < 0xDC00) str == "\xDC00\xD800\xD800\x16805\x16A06\&z" + log "uncons" + assertEqual + { actual: SCP.uncons str + , expected: Just {head: cp 0x61, tail: "\xDC00\xD800\xD800\x16805\x16A06\&z"} + } + assertEqual + { actual: SCP.uncons (SCP.drop 1 str) + , expected: Just {head: cp 0xDC00, tail: "\xD800\xD800\x16805\x16A06\&z"} + } + assertEqual + { actual: SCP.uncons (SCP.drop 2 str) + , expected: Just {head: cp 0xD800, tail: "\xD800\x16805\x16A06\&z"} + } + assertEqual + { actual: SCP.uncons (SCP.drop 3 str) + , expected: Just {head: cp 0xD800, tail: "\x16805\x16A06\&z"} + } + assertEqual + { actual: SCP.uncons (SCP.drop 4 str) + , expected: Just {head: cp 0x16805, tail: "\x16A06\&z"} + } + assertEqual + { actual: SCP.uncons (SCP.drop 5 str) + , expected: Just {head: cp 0x16A06, tail: "z"} + } + assertEqual + { actual: SCP.uncons (SCP.drop 6 str) + , expected: Just {head: cp 0x7A, tail: ""} + } + assertEqual + { actual: SCP.uncons "" + , expected: Nothing + } + + log "length" + assertEqual + { actual: SCP.length "" + , expected: 0 + } + assertEqual + { actual: SCP.length "a" + , expected: 1 + } + assertEqual + { actual: SCP.length "ab" + , expected: 2 + } + assertEqual + { actual: SCP.length str + , expected: 7 + } + + log "countPrefix" + assertEqual + { actual: SCP.countPrefix (\_ -> true) "" + , expected: 0 + } + assertEqual + { actual: SCP.countPrefix (\_ -> false) str + , expected: 0 + } + assertEqual + { actual: SCP.countPrefix (\_ -> true) str + , expected: 7 + } + assertEqual + { actual: SCP.countPrefix (\x -> fromEnum x < 0xFFFF) str + , expected: 4 + } + assertEqual + { actual: SCP.countPrefix (\x -> fromEnum x < 0xDC00) str + , expected: 1 + } log "indexOf" - assert $ indexOf (Pattern "") "" == Just 0 - assert $ indexOf (Pattern "") str == Just 0 - assert $ indexOf (Pattern str) str == Just 0 - assert $ indexOf (Pattern "a") str == Just 0 - assert $ indexOf (Pattern "\xDC00\xD800\xD800") str == Just 1 - assert $ indexOf (Pattern "\xD800") str == Just 2 - assert $ indexOf (Pattern "\xD800\xD800") str == Just 2 - assert $ indexOf (Pattern "\xD800\xD81A") str == Just 3 - assert $ indexOf (Pattern "\xD800\x16805") str == Just 3 - assert $ indexOf (Pattern "\x16805") str == Just 4 - assert $ indexOf (Pattern "\x16A06") str == Just 5 - assert $ indexOf (Pattern "z") str == Just 6 - assert $ indexOf (Pattern "\0") str == Nothing - assert $ indexOf (Pattern "\xD81A") str == Just 4 + assertEqual + { actual: SCP.indexOf (Pattern "") "" + , expected: Just 0 + } + assertEqual + { actual: SCP.indexOf (Pattern "") str + , expected: Just 0 + } + assertEqual + { actual: SCP.indexOf (Pattern str) str + , expected: Just 0 + } + assertEqual + { actual: SCP.indexOf (Pattern "a") str + , expected: Just 0 + } + assertEqual + { actual: SCP.indexOf (Pattern "\xDC00\xD800\xD800") str + , expected: Just 1 + } + assertEqual + { actual: SCP.indexOf (Pattern "\xD800") str + , expected: Just 2 + } + assertEqual + { actual: SCP.indexOf (Pattern "\xD800\xD800") str + , expected: Just 2 + } + assertEqual + { actual: SCP.indexOf (Pattern "\xD800\xD81A") str + , expected: Just 3 + } + assertEqual + { actual: SCP.indexOf (Pattern "\xD800\x16805") str + , expected: Just 3 + } + assertEqual + { actual: SCP.indexOf (Pattern "\x16805") str + , expected: Just 4 + } + assertEqual + { actual: SCP.indexOf (Pattern "\x16A06") str + , expected: Just 5 + } + assertEqual + { actual: SCP.indexOf (Pattern "z") str + , expected: Just 6 + } + assertEqual + { actual: SCP.indexOf (Pattern "\0") str + , expected: Nothing + } + assertEqual + { actual: SCP.indexOf (Pattern "\xD81A") str + , expected: Just 4 + } log "indexOf'" - assert $ indexOf' (Pattern "") 0 "" == Just 0 - assert $ indexOf' (Pattern str) 0 str == Just 0 - assert $ indexOf' (Pattern str) 1 str == Nothing - assert $ indexOf' (Pattern "a") 0 str == Just 0 - assert $ indexOf' (Pattern "a") 1 str == Nothing - assert $ indexOf' (Pattern "z") 0 str == Just 6 - assert $ indexOf' (Pattern "z") 1 str == Just 6 - assert $ indexOf' (Pattern "z") 2 str == Just 6 - assert $ indexOf' (Pattern "z") 3 str == Just 6 - assert $ indexOf' (Pattern "z") 4 str == Just 6 - assert $ indexOf' (Pattern "z") 5 str == Just 6 - assert $ indexOf' (Pattern "z") 6 str == Just 6 - assert $ indexOf' (Pattern "z") 7 str == Nothing + assertEqual + { actual: SCP.indexOf' (Pattern "") 0 "" + , expected: Just 0 + } + assertEqual + { actual: SCP.indexOf' (Pattern str) 0 str + , expected: Just 0 + } + assertEqual + { actual: SCP.indexOf' (Pattern str) 1 str + , expected: Nothing + } + assertEqual + { actual: SCP.indexOf' (Pattern "a") 0 str + , expected: Just 0 + } + assertEqual + { actual: SCP.indexOf' (Pattern "a") 1 str + , expected: Nothing + } + assertEqual + { actual: SCP.indexOf' (Pattern "z") 0 str + , expected: Just 6 + } + assertEqual + { actual: SCP.indexOf' (Pattern "z") 1 str + , expected: Just 6 + } + assertEqual + { actual: SCP.indexOf' (Pattern "z") 2 str + , expected: Just 6 + } + assertEqual + { actual: SCP.indexOf' (Pattern "z") 3 str + , expected: Just 6 + } + assertEqual + { actual: SCP.indexOf' (Pattern "z") 4 str + , expected: Just 6 + } + assertEqual + { actual: SCP.indexOf' (Pattern "z") 5 str + , expected: Just 6 + } + assertEqual + { actual: SCP.indexOf' (Pattern "z") 6 str + , expected: Just 6 + } + assertEqual + { actual: SCP.indexOf' (Pattern "z") 7 str + , expected: Nothing + } log "lastIndexOf" - assert $ lastIndexOf (Pattern "") "" == Just 0 - assert $ lastIndexOf (Pattern "") str == Just 7 - assert $ lastIndexOf (Pattern str) str == Just 0 - assert $ lastIndexOf (Pattern "a") str == Just 0 - assert $ lastIndexOf (Pattern "\xDC00\xD800\xD800") str == Just 1 - assert $ lastIndexOf (Pattern "\xD800") str == Just 3 - assert $ lastIndexOf (Pattern "\xD800\xD800") str == Just 2 - assert $ lastIndexOf (Pattern "\xD800\xD81A") str == Just 3 - assert $ lastIndexOf (Pattern "\xD800\x16805") str == Just 3 - assert $ lastIndexOf (Pattern "\x16805") str == Just 4 - assert $ lastIndexOf (Pattern "\x16A06") str == Just 5 - assert $ lastIndexOf (Pattern "z") str == Just 6 - assert $ lastIndexOf (Pattern "\0") str == Nothing - assert $ lastIndexOf (Pattern "\xD81A") str == Just 5 + assertEqual + { actual: SCP.lastIndexOf (Pattern "") "" + , expected: Just 0 + } + assertEqual + { actual: SCP.lastIndexOf (Pattern "") str + , expected: Just 7 + } + assertEqual + { actual: SCP.lastIndexOf (Pattern str) str + , expected: Just 0 + } + assertEqual + { actual: SCP.lastIndexOf (Pattern "a") str + , expected: Just 0 + } + assertEqual + { actual: SCP.lastIndexOf (Pattern "\xDC00\xD800\xD800") str + , expected: Just 1 + } + assertEqual + { actual: SCP.lastIndexOf (Pattern "\xD800") str + , expected: Just 3 + } + assertEqual + { actual: SCP.lastIndexOf (Pattern "\xD800\xD800") str + , expected: Just 2 + } + assertEqual + { actual: SCP.lastIndexOf (Pattern "\xD800\xD81A") str + , expected: Just 3 + } + assertEqual + { actual: SCP.lastIndexOf (Pattern "\xD800\x16805") str + , expected: Just 3 + } + assertEqual + { actual: SCP.lastIndexOf (Pattern "\x16805") str + , expected: Just 4 + } + assertEqual + { actual: SCP.lastIndexOf (Pattern "\x16A06") str + , expected: Just 5 + } + assertEqual + { actual: SCP.lastIndexOf (Pattern "z") str + , expected: Just 6 + } + assertEqual + { actual: SCP.lastIndexOf (Pattern "\0") str + , expected: Nothing + } + assertEqual + { actual: SCP.lastIndexOf (Pattern "\xD81A") str + , expected: Just 5 + } log "lastIndexOf'" - assert $ lastIndexOf' (Pattern "") 0 "" == Just 0 - assert $ lastIndexOf' (Pattern str) 0 str == Just 0 - assert $ lastIndexOf' (Pattern str) 1 str == Just 0 - assert $ lastIndexOf' (Pattern "a") 0 str == Just 0 - assert $ lastIndexOf' (Pattern "a") 7 str == Just 0 - assert $ lastIndexOf' (Pattern "z") 0 str == Nothing - assert $ lastIndexOf' (Pattern "z") 1 str == Nothing - assert $ lastIndexOf' (Pattern "z") 2 str == Nothing - assert $ lastIndexOf' (Pattern "z") 3 str == Nothing - assert $ lastIndexOf' (Pattern "z") 4 str == Nothing - assert $ lastIndexOf' (Pattern "z") 5 str == Nothing - assert $ lastIndexOf' (Pattern "z") 6 str == Just 6 - assert $ lastIndexOf' (Pattern "z") 7 str == Just 6 - assert $ lastIndexOf' (Pattern "\xD800") 7 str == Just 3 - assert $ lastIndexOf' (Pattern "\xD800") 6 str == Just 3 - assert $ lastIndexOf' (Pattern "\xD800") 5 str == Just 3 - assert $ lastIndexOf' (Pattern "\xD800") 4 str == Just 3 - assert $ lastIndexOf' (Pattern "\xD800") 3 str == Just 3 - assert $ lastIndexOf' (Pattern "\xD800") 2 str == Just 2 - assert $ lastIndexOf' (Pattern "\xD800") 1 str == Nothing - assert $ lastIndexOf' (Pattern "\xD800") 0 str == Nothing - assert $ lastIndexOf' (Pattern "\x16A06") 7 str == Just 5 - assert $ lastIndexOf' (Pattern "\x16A06") 6 str == Just 5 - assert $ lastIndexOf' (Pattern "\x16A06") 5 str == Just 5 - assert $ lastIndexOf' (Pattern "\x16A06") 4 str == Nothing - assert $ lastIndexOf' (Pattern "\x16A06") 3 str == Nothing + assertEqual + { actual: SCP.lastIndexOf' (Pattern "") 0 "" + , expected: Just 0 + } + assertEqual + { actual: SCP.lastIndexOf' (Pattern str) 0 str + , expected: Just 0 + } + assertEqual + { actual: SCP.lastIndexOf' (Pattern str) 1 str + , expected: Just 0 + } + assertEqual + { actual: SCP.lastIndexOf' (Pattern "a") 0 str + , expected: Just 0 + } + assertEqual + { actual: SCP.lastIndexOf' (Pattern "a") 7 str + , expected: Just 0 + } + assertEqual + { actual: SCP.lastIndexOf' (Pattern "z") 0 str + , expected: Nothing + } + assertEqual + { actual: SCP.lastIndexOf' (Pattern "z") 1 str + , expected: Nothing + } + assertEqual + { actual: SCP.lastIndexOf' (Pattern "z") 2 str + , expected: Nothing + } + assertEqual + { actual: SCP.lastIndexOf' (Pattern "z") 3 str + , expected: Nothing + } + assertEqual + { actual: SCP.lastIndexOf' (Pattern "z") 4 str + , expected: Nothing + } + assertEqual + { actual: SCP.lastIndexOf' (Pattern "z") 5 str + , expected: Nothing + } + assertEqual + { actual: SCP.lastIndexOf' (Pattern "z") 6 str + , expected: Just 6 + } + assertEqual + { actual: SCP.lastIndexOf' (Pattern "z") 7 str + , expected: Just 6 + } + assertEqual + { actual: SCP.lastIndexOf' (Pattern "\xD800") 7 str + , expected: Just 3 + } + assertEqual + { actual: SCP.lastIndexOf' (Pattern "\xD800") 6 str + , expected: Just 3 + } + assertEqual + { actual: SCP.lastIndexOf' (Pattern "\xD800") 5 str + , expected: Just 3 + } + assertEqual + { actual: SCP.lastIndexOf' (Pattern "\xD800") 4 str + , expected: Just 3 + } + assertEqual + { actual: SCP.lastIndexOf' (Pattern "\xD800") 3 str + , expected: Just 3 + } + assertEqual + { actual: SCP.lastIndexOf' (Pattern "\xD800") 2 str + , expected: Just 2 + } + assertEqual + { actual: SCP.lastIndexOf' (Pattern "\xD800") 1 str + , expected: Nothing + } + assertEqual + { actual: SCP.lastIndexOf' (Pattern "\xD800") 0 str + , expected: Nothing + } + assertEqual + { actual: SCP.lastIndexOf' (Pattern "\x16A06") 7 str + , expected: Just 5 + } + assertEqual + { actual: SCP.lastIndexOf' (Pattern "\x16A06") 6 str + , expected: Just 5 + } + assertEqual + { actual: SCP.lastIndexOf' (Pattern "\x16A06") 5 str + , expected: Just 5 + } + assertEqual + { actual: SCP.lastIndexOf' (Pattern "\x16A06") 4 str + , expected: Nothing + } + assertEqual + { actual: SCP.lastIndexOf' (Pattern "\x16A06") 3 str + , expected: Nothing + } - log "length" - assert $ length "" == 0 - assert $ length "a" == 1 - assert $ length "ab" == 2 - assert $ length str == 7 + log "take" + assertEqual + { actual: SCP.take (-1) str + , expected: "" + } + assertEqual + { actual: SCP.take 0 str + , expected: "" + } + assertEqual + { actual: SCP.take 1 str + , expected: "a" + } + assertEqual + { actual: SCP.take 2 str + , expected: "a\xDC00" + } + assertEqual + { actual: SCP.take 3 str + , expected: "a\xDC00\xD800" + } + assertEqual + { actual: SCP.take 4 str + , expected: "a\xDC00\xD800\xD800" + } + assertEqual + { actual: SCP.take 5 str + , expected: "a\xDC00\xD800\xD800\x16805" + } + assertEqual + { actual: SCP.take 6 str + , expected: "a\xDC00\xD800\xD800\x16805\x16A06" + } + assertEqual + { actual: SCP.take 7 str + , expected: str + } + assertEqual + { actual: SCP.take 8 str + , expected: str + } - log "singleton" - assert $ (singleton <$> codePointFromInt 0x30) == Just "0" - assert $ (singleton <$> codePointFromInt 0x16805) == Just "\x16805" + log "takeWhile" + assertEqual + { actual: SCP.takeWhile (\_ -> true) str + , expected: str + } + assertEqual + { actual: SCP.takeWhile (\_ -> false) str + , expected: "" + } + assertEqual + { actual: SCP.takeWhile (\c -> fromEnum c < 0xFFFF) str + , expected: "a\xDC00\xD800\xD800" + } + assertEqual + { actual: SCP.takeWhile (\c -> fromEnum c < 0xDC00) str + , expected: "a" + } - log "splitAt" - let testSplitAt i s r = - assert $ case splitAt i s of - { 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 "drop" + assertEqual + { actual: SCP.drop (-1) str + , expected: str + } + assertEqual + { actual: SCP.drop 0 str + , expected: str + } + assertEqual + { actual: SCP.drop 1 str + , expected: "\xDC00\xD800\xD800\x16805\x16A06\&z" + } + assertEqual + { actual: SCP.drop 2 str + , expected: "\xD800\xD800\x16805\x16A06\&z" + } + assertEqual + { actual: SCP.drop 3 str + , expected: "\xD800\x16805\x16A06\&z" + } + assertEqual + { actual: SCP.drop 4 str + , expected: "\x16805\x16A06\&z" + } + assertEqual + { actual: SCP.drop 5 str + , expected: "\x16A06\&z" + } + assertEqual + { actual: SCP.drop 6 str + , expected: "z" + } + assertEqual + { actual: SCP.drop 7 str + , expected: "" + } + assertEqual + { actual: SCP.drop 8 str + , expected: "" + } - log "take" - assert $ take (-1) str == "" - assert $ take 0 str == "" - assert $ take 1 str == "a" - assert $ take 2 str == "a\xDC00" - assert $ take 3 str == "a\xDC00\xD800" - assert $ take 4 str == "a\xDC00\xD800\xD800" - assert $ take 5 str == "a\xDC00\xD800\xD800\x16805" - assert $ take 6 str == "a\xDC00\xD800\xD800\x16805\x16A06" - assert $ take 7 str == str - assert $ take 8 str == str + log "dropWhile" + assertEqual + { actual: SCP.dropWhile (\_ -> true) str + , expected: "" + } + assertEqual + { actual: SCP.dropWhile (\_ -> false) str + , expected: str + } + assertEqual + { actual: SCP.dropWhile (\c -> fromEnum c < 0xFFFF) str + , expected: "\x16805\x16A06\&z" + } + assertEqual + { actual: SCP.dropWhile (\c -> fromEnum c < 0xDC00) str + , expected: "\xDC00\xD800\xD800\x16805\x16A06\&z" + } - log "takeWhile" - assert $ takeWhile (\_ -> true) str == str - assert $ takeWhile (\_ -> false) str == "" - assert $ takeWhile (\c -> codePointToInt c < 0xFFFF) str == "a\xDC00\xD800\xD800" - assert $ takeWhile (\c -> codePointToInt c < 0xDC00) str == "a" + log "splitAt" + assertEqual + { actual: SCP.splitAt 0 "" + , expected: {before: "", after: "" } + } + assertEqual + { actual: SCP.splitAt 1 "" + , expected: {before: "", after: "" } + } + assertEqual + { actual: SCP.splitAt 0 "a" + , expected: {before: "", after: "a"} + } + assertEqual + { actual: SCP.splitAt 1 "ab" + , expected: {before: "a", after: "b"} + } + assertEqual + { actual: SCP.splitAt 3 "aabcc" + , expected: {before: "aab", after: "cc"} + } + assertEqual + { actual: SCP.splitAt (-1) "abc" + , expected: {before: "", after: "abc"} + } + assertEqual + { actual: SCP.splitAt 0 str + , expected: {before: "", after: str} + } + assertEqual + { actual: SCP.splitAt 1 str + , expected: {before: "a", after: "\xDC00\xD800\xD800\x16805\x16A06\&z"} + } + assertEqual + { actual: SCP.splitAt 2 str + , expected: {before: "a\xDC00", after: "\xD800\xD800\x16805\x16A06\&z"} + } + assertEqual + { actual: SCP.splitAt 3 str + , expected: {before: "a\xDC00\xD800", after: "\xD800\x16805\x16A06\&z"} + } + assertEqual + { actual: SCP.splitAt 4 str + , expected: {before: "a\xDC00\xD800\xD800", after: "\x16805\x16A06\&z"} + } + assertEqual + { actual: SCP.splitAt 5 str + , expected: {before: "a\xDC00\xD800\xD800\x16805", after: "\x16A06\&z"} + } + assertEqual + { actual: SCP.splitAt 6 str + , expected: {before: "a\xDC00\xD800\xD800\x16805\x16A06", after: "z"} + } + assertEqual + { actual: SCP.splitAt 7 str + , expected: {before: str, after: ""} + } + assertEqual + { actual: SCP.splitAt 8 str + , expected: {before: str, after: ""} + } - log "uncons" - let testUncons s res = - assert $ case uncons s of - Nothing -> - isNothing res - Just { head, tail } -> - maybe false (\r -> - r.head == codePointToInt head && r.tail == tail) res - - testUncons str $ Just {head: 0x61, tail: "\xDC00\xD800\xD800\x16805\x16A06\&z"} - testUncons (drop 1 str) $ Just {head: 0xDC00, tail: "\xD800\xD800\x16805\x16A06\&z"} - testUncons (drop 2 str) $ Just {head: 0xD800, tail: "\xD800\x16805\x16A06\&z"} - testUncons (drop 3 str) $ Just {head: 0xD800, tail: "\x16805\x16A06\&z"} - testUncons (drop 4 str) $ Just {head: 0x16805, tail: "\x16A06\&z"} - testUncons (drop 5 str) $ Just {head: 0x16A06, tail: "z"} - testUncons (drop 6 str) $ Just {head: 0x7A, tail: ""} - testUncons "" Nothing +cp :: Int -> SCP.CodePoint +cp = unsafePartial fromJust <<< toEnum diff --git a/test/Test/Data/String/CodeUnits.purs b/test/Test/Data/String/CodeUnits.purs new file mode 100644 index 0000000..eb35d15 --- /dev/null +++ b/test/Test/Data/String/CodeUnits.purs @@ -0,0 +1,456 @@ +module Test.Data.String.CodeUnits (testStringCodeUnits) where + +import Prelude + +import Data.Enum (fromEnum) +import Data.Maybe (Maybe(..), isNothing) +import Data.String.CodeUnits as SCU +import Data.String.Pattern (Pattern(..)) +import Effect (Effect) +import Effect.Console (log) +import Test.Assert (assert, assertEqual) + +testStringCodeUnits :: Effect Unit +testStringCodeUnits = do + log "charAt" + assertEqual + { actual: SCU.charAt 0 "" + , expected: Nothing + } + assertEqual + { actual: SCU.charAt 0 "a" + , expected: Just 'a' + } + assertEqual + { actual: SCU.charAt 1 "a" + , expected: Nothing + } + assertEqual + { actual: SCU.charAt 0 "ab" + , expected: Just 'a' + } + assertEqual + { actual: SCU.charAt 1 "ab" + , expected: Just 'b' + } + assertEqual + { actual: SCU.charAt 2 "ab" + , expected: Nothing + } + + log "singleton" + assertEqual + { actual: SCU.singleton 'a' + , expected: "a" + } + + log "charCodeAt" + assertEqual + { actual: (fromEnum <$> SCU.charAt 0 "") + , expected: Nothing + } + assertEqual + { actual: (fromEnum <$> SCU.charAt 0 "a") + , expected: Just 97 + } + assertEqual + { actual: (fromEnum <$> SCU.charAt 1 "a") + , expected: Nothing + } + assertEqual + { actual: (fromEnum <$> SCU.charAt 0 "ab") + , expected: Just 97 + } + assertEqual + { actual: (fromEnum <$> SCU.charAt 1 "ab") + , expected: Just 98 + } + assertEqual + { actual: (fromEnum <$> SCU.charAt 2 "ab") + , expected: Nothing + } + + log "toChar" + assertEqual + { actual: SCU.toChar "" + , expected: Nothing + } + assertEqual + { actual: SCU.toChar "a" + , expected: Just 'a' + } + assertEqual + { actual: SCU.toChar "ab" + , expected: Nothing + } + + log "uncons" + assert $ isNothing (SCU.uncons "") + assertEqual + { actual: SCU.uncons "a" + , expected: Just { head: 'a', tail: "" } + } + assertEqual + { actual: SCU.uncons "ab" + , expected: Just { head: 'a', tail: "b" } + } + + log "takeWhile" + assertEqual + { actual: SCU.takeWhile (\c -> true) "abc" + , expected: "abc" + } + assertEqual + { actual: SCU.takeWhile (\c -> false) "abc" + , expected: "" + } + assertEqual + { actual: SCU.takeWhile (\c -> c /= 'b') "aabbcc" + , expected: "aa" + } + + log "dropWhile" + assertEqual + { actual: SCU.dropWhile (\c -> true) "abc" + , expected: "" + } + assertEqual + { actual: SCU.dropWhile (\c -> false) "abc" + , expected: "abc" + } + assertEqual + { actual: SCU.dropWhile (\c -> c /= 'b') "aabbcc" + , expected: "bbcc" + } + + log "fromCharArray" + assertEqual + { actual: SCU.fromCharArray [] + , expected: "" + } + assertEqual + { actual: SCU.fromCharArray ['a', 'b'] + , expected: "ab" + } + + log "indexOf" + assertEqual + { actual: SCU.indexOf (Pattern "") "" + , expected: Just 0 + } + assertEqual + { actual: SCU.indexOf (Pattern "") "abcd" + , expected: Just 0 + } + assertEqual + { actual: SCU.indexOf (Pattern "bc") "abcd" + , expected: Just 1 + } + assertEqual + { actual: SCU.indexOf (Pattern "cb") "abcd" + , expected: Nothing + } + + log "indexOf'" + assertEqual + { actual: SCU.indexOf' (Pattern "") 0 "" + , expected: Just 0 + } + assertEqual + { actual: SCU.indexOf' (Pattern "") (-1) "ab" + , expected: Nothing + } + assertEqual + { actual: SCU.indexOf' (Pattern "") 0 "ab" + , expected: Just 0 + } + assertEqual + { actual: SCU.indexOf' (Pattern "") 1 "ab" + , expected: Just 1 + } + assertEqual + { actual: SCU.indexOf' (Pattern "") 2 "ab" + , expected: Just 2 + } + assertEqual + { actual: SCU.indexOf' (Pattern "") 3 "ab" + , expected: Nothing + } + assertEqual + { actual: SCU.indexOf' (Pattern "bc") 0 "abcd" + , expected: Just 1 + } + assertEqual + { actual: SCU.indexOf' (Pattern "bc") 1 "abcd" + , expected: Just 1 + } + assertEqual + { actual: SCU.indexOf' (Pattern "bc") 2 "abcd" + , expected: Nothing + } + assertEqual + { actual: SCU.indexOf' (Pattern "cb") 0 "abcd" + , expected: Nothing + } + + log "lastIndexOf" + assertEqual + { actual: SCU.lastIndexOf (Pattern "") "" + , expected: Just 0 + } + assertEqual + { actual: SCU.lastIndexOf (Pattern "") "abcd" + , expected: Just 4 + } + assertEqual + { actual: SCU.lastIndexOf (Pattern "bc") "abcd" + , expected: Just 1 + } + assertEqual + { actual: SCU.lastIndexOf (Pattern "cb") "abcd" + , expected: Nothing + } + + log "lastIndexOf'" + assertEqual + { actual: SCU.lastIndexOf' (Pattern "") 0 "" + , expected: Just 0 + } + assertEqual + { actual: SCU.lastIndexOf' (Pattern "") (-1) "ab" + , expected: Nothing + } + assertEqual + { actual: SCU.lastIndexOf' (Pattern "") 0 "ab" + , expected: Just 0 + } + assertEqual + { actual: SCU.lastIndexOf' (Pattern "") 1 "ab" + , expected: Just 1 + } + assertEqual + { actual: SCU.lastIndexOf' (Pattern "") 2 "ab" + , expected: Just 2 + } + assertEqual + { actual: SCU.lastIndexOf' (Pattern "") 3 "ab" + , expected: Nothing + } + assertEqual + { actual: SCU.lastIndexOf' (Pattern "bc") 0 "abcd" + , expected: Nothing + } + assertEqual + { actual: SCU.lastIndexOf' (Pattern "bc") 1 "abcd" + , expected: Just 1 + } + assertEqual + { actual: SCU.lastIndexOf' (Pattern "bc") 2 "abcd" + , expected: Just 1 + } + assertEqual + { actual: SCU.lastIndexOf' (Pattern "cb") 0 "abcd" + , expected: Nothing + } + + log "length" + assertEqual + { actual: SCU.length "" + , expected: 0 + } + assertEqual + { actual: SCU.length "a" + , expected: 1 + } + assertEqual + { actual: SCU.length "ab" + , expected: 2 + } + + log "take" + assertEqual + { actual: SCU.take 0 "ab" + , expected: "" + } + assertEqual + { actual: SCU.take 1 "ab" + , expected: "a" + } + assertEqual + { actual: SCU.take 2 "ab" + , expected: "ab" + } + assertEqual + { actual: SCU.take 3 "ab" + , expected: "ab" + } + assertEqual + { actual: SCU.take (-1) "ab" + , expected: "" + } + + log "takeRight" + assertEqual + { actual: SCU.takeRight 0 "ab" + , expected: "" + } + assertEqual + { actual: SCU.takeRight 1 "ab" + , expected: "b" + } + assertEqual + { actual: SCU.takeRight 2 "ab" + , expected: "ab" + } + assertEqual + { actual: SCU.takeRight 3 "ab" + , expected: "ab" + } + assertEqual + { actual: SCU.takeRight (-1) "ab" + , expected: "" + } + + log "drop" + assertEqual + { actual: SCU.drop 0 "ab" + , expected: "ab" + } + assertEqual + { actual: SCU.drop 1 "ab" + , expected: "b" + } + assertEqual + { actual: SCU.drop 2 "ab" + , expected: "" + } + assertEqual + { actual: SCU.drop 3 "ab" + , expected: "" + } + assertEqual + { actual: SCU.drop (-1) "ab" + , expected: "ab" + } + + log "dropRight" + assertEqual + { actual: SCU.dropRight 0 "ab" + , expected: "ab" + } + assertEqual + { actual: SCU.dropRight 1 "ab" + , expected: "a" + } + assertEqual + { actual: SCU.dropRight 2 "ab" + , expected: "" + } + assertEqual + { actual: SCU.dropRight 3 "ab" + , expected: "" + } + assertEqual + { actual: SCU.dropRight (-1) "ab" + , expected: "ab" + } + + log "countPrefix" + assertEqual + { actual: SCU.countPrefix (_ == 'a') "" + , expected: 0 + } + assertEqual + { actual: SCU.countPrefix (_ == 'a') "ab" + , expected: 1 + } + assertEqual + { actual: SCU.countPrefix (_ == 'a') "aaab" + , expected: 3 + } + assertEqual + { actual: SCU.countPrefix (_ == 'a') "abaa" + , expected: 1 + } + + log "splitAt" + assertEqual + { actual: SCU.splitAt 1 "" + , expected: {before: "", after: ""} + } + assertEqual + { actual: SCU.splitAt 0 "a" + , expected: {before: "", after: "a"} + } + assertEqual + { actual: SCU.splitAt 1 "a" + , expected: {before: "a", after: ""} + } + assertEqual + { actual: SCU.splitAt 1 "ab" + , expected: {before: "a", after: "b"} + } + assertEqual + { actual: SCU.splitAt 3 "aabcc" + , expected: {before: "aab", after: "cc"} + } + assertEqual + { actual: SCU.splitAt (-1) "abc" + , expected: {before: "", after: "abc"} + } + assertEqual + { actual: SCU.splitAt 10 "Hi" + , expected: {before: "Hi", after: ""} + } + + log "toCharArray" + assertEqual + { actual: SCU.toCharArray "" + , expected: [] + } + assertEqual + { actual: SCU.toCharArray "a" + , expected: ['a'] + } + assertEqual + { actual: SCU.toCharArray "ab" + , expected: ['a', 'b'] + } + + log "slice" + assertEqual + { actual: SCU.slice 0 0 "purescript" + , expected: Just "" + } + assertEqual + { actual: SCU.slice 0 1 "purescript" + , expected: Just "p" + } + assertEqual + { actual: SCU.slice 3 6 "purescript" + , expected: Just "esc" + } + assertEqual + { actual: SCU.slice (-4) (-1) "purescript" + , expected: Just "rip" + } + assertEqual + { actual: SCU.slice (-4) 3 "purescript" + , expected: Nothing -- b' > e' + } + assertEqual + { actual: SCU.slice 1000 3 "purescript" + , expected: Nothing -- b' > e' (subsumes b > l) + } + assertEqual + { actual: SCU.slice 2 (-15) "purescript" + , expected: Nothing -- e' < 0 + } + assertEqual + { actual: SCU.slice (-15) 9 "purescript" + , expected: Nothing -- b' < 0 + } + assertEqual + { actual: SCU.slice 3 1000 "purescript" + , expected: Nothing -- e > l + } diff --git a/test/Test/Data/String/NonEmpty.purs b/test/Test/Data/String/NonEmpty.purs index e1f0983..59a8f22 100644 --- a/test/Test/Data/String/NonEmpty.purs +++ b/test/Test/Data/String/NonEmpty.purs @@ -1,257 +1,220 @@ module Test.Data.String.NonEmpty (testNonEmptyString) where -import Data.String.NonEmpty +import Prelude import Data.Array.NonEmpty as NEA -import Data.Array.Partial as AP -import Data.Foldable (class Foldable, foldl) -import Data.Maybe (Maybe(..), fromJust, isNothing, maybe) -import Data.Semigroup.Foldable (class Foldable1, foldMap1Default) +import Data.Maybe (Maybe(..), fromJust) +import Data.String.NonEmpty (Pattern(..), nes) +import Data.String.NonEmpty as NES +import Data.Symbol (SProxy(..)) import Effect (Effect) import Effect.Console (log) import Partial.Unsafe (unsafePartial) -import Prelude (class Functor, Ordering(..), Unit, append, discard, negate, not, ($), (&&), (/=), (==)) -import Test.Assert (assert) +import Test.Assert (assert, assertEqual) testNonEmptyString :: Effect Unit testNonEmptyString = do - log "fromString" - assert $ fromString "" == Nothing - assert $ fromString "hello" == Just (nes "hello") - - log "fromCharArray" - assert $ fromCharArray [] == Nothing - assert $ fromCharArray ['a', 'b'] == Just (nes "ab") - - log "fromNonEmptyCharArray" - assert $ fromNonEmptyCharArray (NEA.singleton 'b') == singleton 'b' - - log "singleton" - assert $ singleton 'a' == nes "a" - - log "cons" - assert $ cons 'a' "bc" == nes "abc" - assert $ cons 'a' "" == nes "a" - - log "snoc" - assert $ snoc 'c' "ab" == nes "abc" - assert $ snoc 'a' "" == nes "a" - - log "fromFoldable1" - assert $ fromFoldable1 (NEA ['a']) == nes "a" - assert $ fromFoldable1 (NEA ['a', 'b', 'c']) == nes "abc" - - log "charAt" - assert $ charAt 0 (nes "a") == Just 'a' - assert $ charAt 1 (nes "a") == Nothing - assert $ charAt 0 (nes "ab") == Just 'a' - assert $ charAt 1 (nes "ab") == Just 'b' - assert $ charAt 2 (nes "ab") == Nothing - assert $ charAt 2 (nes "Hello") == Just 'l' - assert $ charAt 10 (nes "Hello") == Nothing - - log "charCodeAt" - assert $ charCodeAt 0 (nes "a") == Just 97 - assert $ charCodeAt 1 (nes "a") == Nothing - assert $ charCodeAt 0 (nes "ab") == Just 97 - assert $ charCodeAt 1 (nes "ab") == Just 98 - assert $ charCodeAt 2 (nes "ab") == Nothing - assert $ charCodeAt 2 (nes "5 €") == Just 0x20AC - assert $ charCodeAt 10 (nes "5 €") == Nothing - log "toChar" - assert $ toChar (nes "a") == Just 'a' - assert $ toChar (nes "ab") == Nothing - - log "toCharArray" - assert $ toCharArray (nes "a") == ['a'] - assert $ toCharArray (nes "ab") == ['a', 'b'] - assert $ toCharArray (nes "Hello☺\n") == ['H','e','l','l','o','☺','\n'] - - log "toNonEmptyCharArray" - assert $ toNonEmptyCharArray (nes "ab") - == unsafePartial fromJust (NEA.fromArray ['a', 'b']) + log "fromString" + assertEqual + { actual: NES.fromString "" + , expected: Nothing + } + assertEqual + { actual: NES.fromString "hello" + , expected: Just (nes (SProxy :: SProxy "hello")) + } + + log "toString" + assertEqual + { actual: (NES.toString <$> NES.fromString "hello") + , expected: Just "hello" + } log "appendString" - assert $ appendString (nes "Hello") " world" == nes "Hello world" - assert $ appendString (nes "Hello") "" == nes "Hello" + assertEqual + { actual: NES.appendString (nes (SProxy :: SProxy "Hello")) " world" + , expected: nes (SProxy :: SProxy "Hello world") + } + assertEqual + { actual: NES.appendString (nes (SProxy :: SProxy "Hello")) "" + , expected: nes (SProxy :: SProxy "Hello") + } log "prependString" - assert $ prependString "be" (nes "fore") == nes "before" - assert $ prependString "" (nes "fore") == nes "fore" - - log "uncons" - assert - let m = uncons (nes "a") - in m.head == 'a' && m.tail == Nothing - assert $ - let m = uncons (nes "Hello World") - in m.head == 'H' && m.tail == Just (nes "ello World") - - log "takeWhile" - assert $ takeWhile (\c -> true) (nes "abc") == Just (nes "abc") - assert $ takeWhile (\c -> false) (nes "abc") == Nothing - assert $ takeWhile (\c -> c /= 'b') (nes "aabbcc") == Just (nes "aa") - assert $ takeWhile (_ /= ':') (nes "http://purescript.org") == Just (nes "http") - assert $ takeWhile (_ == 'a') (nes "xyz") == Nothing - - log "dropWhile" - assert $ dropWhile (\c -> true) (nes "abc") == Nothing - assert $ dropWhile (\c -> false) (nes "abc") == Just (nes "abc") - assert $ dropWhile (\c -> c /= 'b') (nes "aabbcc") == Just (nes "bbcc") - assert $ dropWhile (_ /= '.') (nes "Test.purs") == Just (nes ".purs") - - log "stripPrefix" - assert $ stripPrefix (Pattern "") (nes "abc") == Just (nes "abc") - assert $ stripPrefix (Pattern "a") (nes "abc") == Just (nes "bc") - assert $ stripPrefix (Pattern "abc") (nes "abc") == Nothing - assert $ stripPrefix (Pattern "!") (nes "abc") == Nothing - assert $ stripPrefix (Pattern "http:") (nes "http://purescript.org") == Just (nes "//purescript.org") - assert $ stripPrefix (Pattern "http:") (nes "https://purescript.org") == Nothing - assert $ stripPrefix (Pattern "Hello!") (nes "Hello!") == Nothing - - log "stripSuffix" - assert $ stripSuffix (Pattern ".exe") (nes "purs.exe") == Just (nes "purs") - assert $ stripSuffix (Pattern ".exe") (nes "purs") == Nothing - assert $ stripSuffix (Pattern "Hello!") (nes "Hello!") == Nothing + assertEqual + { actual: NES.prependString "be" (nes (SProxy :: SProxy "fore")) + , expected: nes (SProxy :: SProxy "before") + } + assertEqual + { actual: NES.prependString "" (nes (SProxy :: SProxy "fore")) + , expected: nes (SProxy :: SProxy "fore") + } log "contains" - assert $ contains (Pattern "") (nes "abcd") - assert $ contains (Pattern "bc") (nes "abcd") - assert $ not (contains (Pattern "cb") (nes "abcd")) - assert $ contains (Pattern "needle") (nes "haystack with needle") == true - assert $ contains (Pattern "needle") (nes "haystack") == false - - log "indexOf" - assert $ indexOf (Pattern "") (nes "abcd") == Just 0 - assert $ indexOf (Pattern "bc") (nes "abcd") == Just 1 - assert $ indexOf (Pattern "cb") (nes "abcd") == Nothing - - log "indexOf'" - assert $ indexOf' (Pattern "") (-1) (nes "ab") == Nothing - assert $ indexOf' (Pattern "") 0 (nes "ab") == Just 0 - assert $ indexOf' (Pattern "") 1 (nes "ab") == Just 1 - assert $ indexOf' (Pattern "") 2 (nes "ab") == Just 2 - assert $ indexOf' (Pattern "") 3 (nes "ab") == Nothing - assert $ indexOf' (Pattern "bc") 0 (nes "abcd") == Just 1 - assert $ indexOf' (Pattern "bc") 1 (nes "abcd") == Just 1 - assert $ indexOf' (Pattern "bc") 2 (nes "abcd") == Nothing - assert $ indexOf' (Pattern "cb") 0 (nes "abcd") == Nothing - - log "lastIndexOf" - assert $ lastIndexOf (Pattern "") (nes "abcd") == Just 4 - assert $ lastIndexOf (Pattern "bc") (nes "abcd") == Just 1 - assert $ lastIndexOf (Pattern "cb") (nes "abcd") == Nothing - - log "lastIndexOf'" - assert $ lastIndexOf' (Pattern "") (-1) (nes "ab") == Nothing - assert $ lastIndexOf' (Pattern "") 0 (nes "ab") == Just 0 - assert $ lastIndexOf' (Pattern "") 1 (nes "ab") == Just 1 - assert $ lastIndexOf' (Pattern "") 2 (nes "ab") == Just 2 - assert $ lastIndexOf' (Pattern "") 3 (nes "ab") == Nothing - assert $ lastIndexOf' (Pattern "bc") 0 (nes "abcd") == Nothing - assert $ lastIndexOf' (Pattern "bc") 1 (nes "abcd") == Just 1 - assert $ lastIndexOf' (Pattern "bc") 2 (nes "abcd") == Just 1 - assert $ lastIndexOf' (Pattern "cb") 0 (nes "abcd") == Nothing - - log "length" - assert $ length (nes "a") == 1 - assert $ length (nes "ab") == 2 + assert $ NES.contains (Pattern "") (nes (SProxy :: SProxy "abcd")) + assert $ NES.contains (Pattern "bc") (nes (SProxy :: SProxy "abcd")) + assert $ not NES.contains (Pattern "cb") (nes (SProxy :: SProxy "abcd")) + assert $ NES.contains (Pattern "needle") (nes (SProxy :: SProxy "haystack with needle")) + assert $ not NES.contains (Pattern "needle") (nes (SProxy :: SProxy "haystack")) log "localeCompare" - assert $ localeCompare (nes "a") (nes "a") == EQ - assert $ localeCompare (nes "a") (nes "b") == LT - assert $ localeCompare (nes "b") (nes "a") == GT + assertEqual + { actual: NES.localeCompare (nes (SProxy :: SProxy "a")) (nes (SProxy :: SProxy "a")) + , expected: EQ + } + assertEqual + { actual: NES.localeCompare (nes (SProxy :: SProxy "a")) (nes (SProxy :: SProxy "b")) + , expected: LT + } + assertEqual + { actual: NES.localeCompare (nes (SProxy :: SProxy "b")) (nes (SProxy :: SProxy "a")) + , expected: GT + } log "replace" - assert $ replace (Pattern "b") (NonEmptyReplacement (nes "!")) (nes "abc") == nes "a!c" - assert $ replace (Pattern "b") (NonEmptyReplacement (nes "!")) (nes "abbc") == nes "a!bc" - assert $ replace (Pattern "d") (NonEmptyReplacement (nes "!")) (nes "abc") == nes "abc" + assertEqual + { actual: NES.replace (Pattern "b") (NES.NonEmptyReplacement (nes (SProxy :: SProxy "!"))) (nes (SProxy :: SProxy "abc")) + , expected: nes (SProxy :: SProxy "a!c") + } + assertEqual + { actual: NES.replace (Pattern "b") (NES.NonEmptyReplacement (nes (SProxy :: SProxy "!"))) (nes (SProxy :: SProxy "abbc")) + , expected: nes (SProxy :: SProxy "a!bc") + } + assertEqual + { actual: NES.replace (Pattern "d") (NES.NonEmptyReplacement (nes (SProxy :: SProxy "!"))) (nes (SProxy :: SProxy "abc")) + , expected: nes (SProxy :: SProxy "abc") + } log "replaceAll" - assert $ replaceAll (Pattern "[b]") (NonEmptyReplacement (nes "!")) (nes "a[b]c") == nes "a!c" - assert $ replaceAll (Pattern "[b]") (NonEmptyReplacement (nes "!")) (nes "a[b]c[b]") == nes "a!c!" - assert $ replaceAll (Pattern "x") (NonEmptyReplacement (nes "!")) (nes "abc") == nes "abc" - - log "take" - assert $ take 0 (nes "ab") == Nothing - assert $ take 1 (nes "ab") == Just (nes "a") - assert $ take 2 (nes "ab") == Just (nes "ab") - assert $ take 3 (nes "ab") == Just (nes "ab") - assert $ take (-1) (nes "ab") == Nothing - - log "takeRight" - assert $ takeRight 0 (nes "ab") == Nothing - assert $ takeRight 1 (nes "ab") == Just (nes "b") - assert $ takeRight 2 (nes "ab") == Just (nes "ab") - assert $ takeRight 3 (nes "ab") == Just (nes "ab") - assert $ takeRight (-1) (nes "ab") == Nothing + assertEqual + { actual: NES.replaceAll (Pattern "[b]") (NES.NonEmptyReplacement (nes (SProxy :: SProxy "!"))) (nes (SProxy :: SProxy "a[b]c")) + , expected: nes (SProxy :: SProxy "a!c") + } + assertEqual + { actual: NES.replaceAll (Pattern "[b]") (NES.NonEmptyReplacement (nes (SProxy :: SProxy "!"))) (nes (SProxy :: SProxy "a[b]c[b]")) + , expected: nes (SProxy :: SProxy "a!c!") + } + assertEqual + { actual: NES.replaceAll (Pattern "x") (NES.NonEmptyReplacement (nes (SProxy :: SProxy "!"))) (nes (SProxy :: SProxy "abc")) + , expected: nes (SProxy :: SProxy "abc") + } - log "drop" - assert $ drop 0 (nes "ab") == Just (nes "ab") - assert $ drop 1 (nes "ab") == Just (nes "b") - assert $ drop 2 (nes "ab") == Nothing - assert $ drop 3 (nes "ab") == Nothing - assert $ drop (-1) (nes "ab") == Just (nes "ab") - - log "dropRight" - assert $ dropRight 0 (nes "ab") == Just (nes "ab") - assert $ dropRight 1 (nes "ab") == Just (nes "a") - assert $ dropRight 2 (nes "ab") == Nothing - assert $ dropRight 3 (nes "ab") == Nothing - assert $ dropRight (-1) (nes "ab") == Just (nes "ab") - - log "countPrefix" - assert $ countPrefix (_ == 'a') (nes "ab") == 1 - assert $ countPrefix (_ == 'a') (nes "aaab") == 3 - assert $ countPrefix (_ == 'a') (nes "abaa") == 1 - assert $ countPrefix (_ == 'c') (nes "abaa") == 0 + log "stripPrefix" + assertEqual + { actual: NES.stripPrefix (Pattern "") (nes (SProxy :: SProxy "abc")) + , expected: Just (nes (SProxy :: SProxy "abc")) + } + assertEqual + { actual: NES.stripPrefix (Pattern "a") (nes (SProxy :: SProxy "abc")) + , expected: Just (nes (SProxy :: SProxy "bc")) + } + assertEqual + { actual: NES.stripPrefix (Pattern "abc") (nes (SProxy :: SProxy "abc")) + , expected: Nothing + } + assertEqual + { actual: NES.stripPrefix (Pattern "!") (nes (SProxy :: SProxy "abc")) + , expected: Nothing + } + assertEqual + { actual: NES.stripPrefix (Pattern "http:") (nes (SProxy :: SProxy "http://purescript.org")) + , expected: Just (nes (SProxy :: SProxy "//purescript.org")) + } + assertEqual + { actual: NES.stripPrefix (Pattern "http:") (nes (SProxy :: SProxy "https://purescript.org")) + , expected: Nothing + } + assertEqual + { actual: NES.stripPrefix (Pattern "Hello!") (nes (SProxy :: SProxy "Hello!")) + , expected: Nothing + } - log "splitAt" - let - testSplitAt i str res = - assert $ case splitAt i str of - { 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 "stripSuffix" + assertEqual + { actual: NES.stripSuffix (Pattern ".exe") (nes (SProxy :: SProxy "purs.exe")) + , expected: Just (nes (SProxy :: SProxy "purs")) + } + assertEqual + { actual: NES.stripSuffix (Pattern ".exe") (nes (SProxy :: SProxy "purs")) + , expected: Nothing + } + assertEqual + { actual: NES.stripSuffix (Pattern "Hello!") (nes (SProxy :: SProxy "Hello!")) + , expected: Nothing + } log "toLower" - assert $ toLower (nes "bAtMaN") == nes "batman" + assertEqual + { actual: NES.toLower (nes (SProxy :: SProxy "bAtMaN")) + , expected: nes (SProxy :: SProxy "batman") + } log "toUpper" - assert $ toUpper (nes "bAtMaN") == nes "BATMAN" + assertEqual + { actual: NES.toUpper (nes (SProxy :: SProxy "bAtMaN")) + , expected: nes (SProxy :: SProxy "BATMAN") + } log "trim" - assert $ trim (nes " abc ") == Just (nes "abc") - assert $ trim (nes " \n") == Nothing + assertEqual + { actual: NES.trim (nes (SProxy :: SProxy " abc ")) + , expected: Just (nes (SProxy :: SProxy "abc")) + } + assertEqual + { actual: NES.trim (nes (SProxy :: SProxy " \n")) + , expected: Nothing + } log "joinWith" - assert $ joinWith "" [] == "" - assert $ joinWith "" [nes "a", nes "b"] == "ab" - assert $ joinWith "--" [nes "a", nes "b", nes "c"] == "a--b--c" + assertEqual + { actual: NES.joinWith "" [] + , expected: "" + } + assertEqual + { actual: NES.joinWith "" [nes (SProxy :: SProxy "a"), nes (SProxy :: SProxy "b")] + , expected: "ab" + } + assertEqual + { actual: NES.joinWith "--" [nes (SProxy :: SProxy "a"), nes (SProxy :: SProxy "b"), nes (SProxy :: SProxy "c")] + , expected: "a--b--c" + } log "join1With" - assert $ join1With "" (NEA [nes "a", nes "b"]) == nes "ab" - assert $ join1With "--" (NEA [nes "a", nes "b", nes "c"]) == nes "a--b--c" - assert $ join1With ", " (NEA [nes "apple", nes "banana"]) == nes "apple, banana" - assert $ join1With "" (NEA [nes "apple", nes "banana"]) == nes "applebanana" + assertEqual + { actual: NES.join1With "" (nea [nes (SProxy :: SProxy "a"), nes (SProxy :: SProxy "b")]) + , expected: nes (SProxy :: SProxy "ab") + } + assertEqual + { actual: NES.join1With "--" (nea [nes (SProxy :: SProxy "a"), nes (SProxy :: SProxy "b"), nes (SProxy :: SProxy "c")]) + , expected: nes (SProxy :: SProxy "a--b--c") + } + assertEqual + { actual: NES.join1With ", " (nea [nes (SProxy :: SProxy "apple"), nes (SProxy :: SProxy "banana")]) + , expected: nes (SProxy :: SProxy "apple, banana") + } + assertEqual + { actual: NES.join1With "" (nea [nes (SProxy :: SProxy "apple"), nes (SProxy :: SProxy "banana")]) + , expected: nes (SProxy :: SProxy "applebanana") + } log "joinWith1" - assert $ joinWith1 (nes " ") (NEA ["a", "b"]) == nes "a b" - assert $ joinWith1 (nes "--") (NEA ["a", "b", "c"]) == nes "a--b--c" - assert $ joinWith1 (nes ", ") (NEA ["apple", "banana"]) == nes "apple, banana" - assert $ joinWith1 (nes "/") (NEA ["a", "b", "", "c", ""]) == nes "a/b//c/" - -nes :: String -> NonEmptyString -nes = unsafePartial unsafeFromString - -newtype NEA a = NEA (Array a) - -derive newtype instance functorNEA :: Functor NEA -derive newtype instance foldableNEA :: Foldable NEA - -instance foldable1NEA :: Foldable1 NEA where - foldMap1 a = foldMap1Default a - fold1 (NEA as) = foldl append (unsafePartial AP.head as) (unsafePartial AP.tail as) + assertEqual + { actual: NES.joinWith1 (nes (SProxy :: SProxy " ")) (nea ["a", "b"]) + , expected: nes (SProxy :: SProxy "a b") + } + assertEqual + { actual: NES.joinWith1 (nes (SProxy :: SProxy "--")) (nea ["a", "b", "c"]) + , expected: nes (SProxy :: SProxy "a--b--c") + } + assertEqual + { actual: NES.joinWith1 (nes (SProxy :: SProxy ", ")) (nea ["apple", "banana"]) + , expected: nes (SProxy :: SProxy "apple, banana") + } + assertEqual + { actual: NES.joinWith1 (nes (SProxy :: SProxy "/")) (nea ["a", "b", "", "c", ""]) + , expected: nes (SProxy :: SProxy "a/b//c/") + } + +nea :: Array ~> NEA.NonEmptyArray +nea = unsafePartial fromJust <<< NEA.fromArray diff --git a/test/Test/Data/String/NonEmpty/CodeUnits.purs b/test/Test/Data/String/NonEmpty/CodeUnits.purs new file mode 100644 index 0000000..fee9b51 --- /dev/null +++ b/test/Test/Data/String/NonEmpty/CodeUnits.purs @@ -0,0 +1,450 @@ +module Test.Data.String.NonEmpty.CodeUnits (testNonEmptyStringCodeUnits) where + +import Prelude + +import Data.Array.NonEmpty as NEA +import Data.Enum (fromEnum) +import Data.Maybe (Maybe(..), fromJust) +import Data.String.NonEmpty (Pattern(..), nes) +import Data.String.NonEmpty.CodeUnits as NESCU +import Data.Symbol (SProxy(..)) +import Effect (Effect) +import Effect.Console (log) +import Partial.Unsafe (unsafePartial) +import Test.Assert (assertEqual) + +testNonEmptyStringCodeUnits :: Effect Unit +testNonEmptyStringCodeUnits = do + + log "fromCharArray" + assertEqual + { actual: NESCU.fromCharArray [] + , expected: Nothing + } + assertEqual + { actual: NESCU.fromCharArray ['a', 'b'] + , expected: Just (nes (SProxy :: SProxy "ab")) + } + + log "fromNonEmptyCharArray" + assertEqual + { actual: NESCU.fromNonEmptyCharArray (NEA.singleton 'b') + , expected: NESCU.singleton 'b' + } + + log "singleton" + assertEqual + { actual: NESCU.singleton 'a' + , expected: nes (SProxy :: SProxy "a") + } + + log "cons" + assertEqual + { actual: NESCU.cons 'a' "bc" + , expected: nes (SProxy :: SProxy "abc") + } + assertEqual + { actual: NESCU.cons 'a' "" + , expected: nes (SProxy :: SProxy "a") + } + + log "snoc" + assertEqual + { actual: NESCU.snoc 'c' "ab" + , expected: nes (SProxy :: SProxy "abc") + } + assertEqual + { actual: NESCU.snoc 'a' "" + , expected: nes (SProxy :: SProxy "a") + } + + log "fromFoldable1" + assertEqual + { actual: NESCU.fromFoldable1 (nea ['a']) + , expected: nes (SProxy :: SProxy "a") + } + assertEqual + { actual: NESCU.fromFoldable1 (nea ['a', 'b', 'c']) + , expected: nes (SProxy :: SProxy "abc") + } + + log "charAt" + assertEqual + { actual: NESCU.charAt 0 (nes (SProxy :: SProxy "a")) + , expected: Just 'a' + } + assertEqual + { actual: NESCU.charAt 1 (nes (SProxy :: SProxy "a")) + , expected: Nothing + } + assertEqual + { actual: NESCU.charAt 0 (nes (SProxy :: SProxy "ab")) + , expected: Just 'a' + } + assertEqual + { actual: NESCU.charAt 1 (nes (SProxy :: SProxy "ab")) + , expected: Just 'b' + } + assertEqual + { actual: NESCU.charAt 2 (nes (SProxy :: SProxy "ab")) + , expected: Nothing + } + assertEqual + { actual: NESCU.charAt 2 (nes (SProxy :: SProxy "Hello")) + , expected: Just 'l' + } + assertEqual + { actual: NESCU.charAt 10 (nes (SProxy :: SProxy "Hello")) + , expected: Nothing + } + + log "charCodeAt" + assertEqual + { actual: fromEnum <$> NESCU.charAt 0 (nes (SProxy :: SProxy "a")) + , expected: Just 97 + } + assertEqual + { actual: fromEnum <$> NESCU.charAt 1 (nes (SProxy :: SProxy "a")) + , expected: Nothing + } + assertEqual + { actual: fromEnum <$> NESCU.charAt 0 (nes (SProxy :: SProxy "ab")) + , expected: Just 97 + } + assertEqual + { actual: fromEnum <$> NESCU.charAt 1 (nes (SProxy :: SProxy "ab")) + , expected: Just 98 + } + assertEqual + { actual: fromEnum <$> NESCU.charAt 2 (nes (SProxy :: SProxy "ab")) + , expected: Nothing + } + assertEqual + { actual: fromEnum <$> NESCU.charAt 2 (nes (SProxy :: SProxy "5 €")) + , expected: Just 0x20AC + } + assertEqual + { actual: fromEnum <$> NESCU.charAt 10 (nes (SProxy :: SProxy "5 €")) + , expected: Nothing + } + + log "toChar" + assertEqual + { actual: NESCU.toChar (nes (SProxy :: SProxy "a")) + , expected: Just 'a' + } + assertEqual + { actual: NESCU.toChar (nes (SProxy :: SProxy "ab")) + , expected: Nothing + } + + log "toCharArray" + assertEqual + { actual: NESCU.toCharArray (nes (SProxy :: SProxy "a")) + , expected: ['a'] + } + assertEqual + { actual: NESCU.toCharArray (nes (SProxy :: SProxy "ab")) + , expected: ['a', 'b'] + } + assertEqual + { actual: NESCU.toCharArray (nes (SProxy :: SProxy "Hello☺\n")) + , expected: ['H','e','l','l','o','☺','\n'] + } + + log "toNonEmptyCharArray" + assertEqual + { actual: NESCU.toNonEmptyCharArray (nes (SProxy :: SProxy "ab")) + , expected: nea ['a', 'b'] + } + + log "uncons" + assertEqual + { actual: NESCU.uncons (nes (SProxy :: SProxy "a")) + , expected: { head: 'a', tail: Nothing } + } + assertEqual + { actual: NESCU.uncons (nes (SProxy :: SProxy "Hello World")) + , expected: { head: 'H', tail: Just (nes (SProxy :: SProxy "ello World")) } + } + + log "takeWhile" + assertEqual + { actual: NESCU.takeWhile (\c -> true) (nes (SProxy :: SProxy "abc")) + , expected: Just (nes (SProxy :: SProxy "abc")) + } + assertEqual + { actual: NESCU.takeWhile (\c -> false) (nes (SProxy :: SProxy "abc")) + , expected: Nothing + } + assertEqual + { actual: NESCU.takeWhile (\c -> c /= 'b') (nes (SProxy :: SProxy "aabbcc")) + , expected: Just (nes (SProxy :: SProxy "aa")) + } + assertEqual + { actual: NESCU.takeWhile (_ /= ':') (nes (SProxy :: SProxy "http://purescript.org")) + , expected: Just (nes (SProxy :: SProxy "http")) + } + assertEqual + { actual: NESCU.takeWhile (_ == 'a') (nes (SProxy :: SProxy "xyz")) + , expected: Nothing + } + + log "dropWhile" + assertEqual + { actual: NESCU.dropWhile (\c -> true) (nes (SProxy :: SProxy "abc")) + , expected: Nothing + } + assertEqual + { actual: NESCU.dropWhile (\c -> false) (nes (SProxy :: SProxy "abc")) + , expected: Just (nes (SProxy :: SProxy "abc")) + } + assertEqual + { actual: NESCU.dropWhile (\c -> c /= 'b') (nes (SProxy :: SProxy "aabbcc")) + , expected: Just (nes (SProxy :: SProxy "bbcc")) + } + assertEqual + { actual: NESCU.dropWhile (_ /= '.') (nes (SProxy :: SProxy "Test.purs")) + , expected: Just (nes (SProxy :: SProxy ".purs")) + } + + log "indexOf" + assertEqual + { actual: NESCU.indexOf (Pattern "") (nes (SProxy :: SProxy "abcd")) + , expected: Just 0 + } + assertEqual + { actual: NESCU.indexOf (Pattern "bc") (nes (SProxy :: SProxy "abcd")) + , expected: Just 1 + } + assertEqual + { actual: NESCU.indexOf (Pattern "cb") (nes (SProxy :: SProxy "abcd")) + , expected: Nothing + } + + log "indexOf'" + assertEqual + { actual: NESCU.indexOf' (Pattern "") (-1) (nes (SProxy :: SProxy "ab")) + , expected: Nothing + } + assertEqual + { actual: NESCU.indexOf' (Pattern "") 0 (nes (SProxy :: SProxy "ab")) + , expected: Just 0 + } + assertEqual + { actual: NESCU.indexOf' (Pattern "") 1 (nes (SProxy :: SProxy "ab")) + , expected: Just 1 + } + assertEqual + { actual: NESCU.indexOf' (Pattern "") 2 (nes (SProxy :: SProxy "ab")) + , expected: Just 2 + } + assertEqual + { actual: NESCU.indexOf' (Pattern "") 3 (nes (SProxy :: SProxy "ab")) + , expected: Nothing + } + assertEqual + { actual: NESCU.indexOf' (Pattern "bc") 0 (nes (SProxy :: SProxy "abcd")) + , expected: Just 1 + } + assertEqual + { actual: NESCU.indexOf' (Pattern "bc") 1 (nes (SProxy :: SProxy "abcd")) + , expected: Just 1 + } + assertEqual + { actual: NESCU.indexOf' (Pattern "bc") 2 (nes (SProxy :: SProxy "abcd")) + , expected: Nothing + } + assertEqual + { actual: NESCU.indexOf' (Pattern "cb") 0 (nes (SProxy :: SProxy "abcd")) + , expected: Nothing + } + + log "lastIndexOf" + assertEqual + { actual: NESCU.lastIndexOf (Pattern "") (nes (SProxy :: SProxy "abcd")) + , expected: Just 4 + } + assertEqual + { actual: NESCU.lastIndexOf (Pattern "bc") (nes (SProxy :: SProxy "abcd")) + , expected: Just 1 + } + assertEqual + { actual: NESCU.lastIndexOf (Pattern "cb") (nes (SProxy :: SProxy "abcd")) + , expected: Nothing + } + + log "lastIndexOf'" + assertEqual + { actual: NESCU.lastIndexOf' (Pattern "") (-1) (nes (SProxy :: SProxy "ab")) + , expected: Nothing + } + assertEqual + { actual: NESCU.lastIndexOf' (Pattern "") 0 (nes (SProxy :: SProxy "ab")) + , expected: Just 0 + } + assertEqual + { actual: NESCU.lastIndexOf' (Pattern "") 1 (nes (SProxy :: SProxy "ab")) + , expected: Just 1 + } + assertEqual + { actual: NESCU.lastIndexOf' (Pattern "") 2 (nes (SProxy :: SProxy "ab")) + , expected: Just 2 + } + assertEqual + { actual: NESCU.lastIndexOf' (Pattern "") 3 (nes (SProxy :: SProxy "ab")) + , expected: Nothing + } + assertEqual + { actual: NESCU.lastIndexOf' (Pattern "bc") 0 (nes (SProxy :: SProxy "abcd")) + , expected: Nothing + } + assertEqual + { actual: NESCU.lastIndexOf' (Pattern "bc") 1 (nes (SProxy :: SProxy "abcd")) + , expected: Just 1 + } + assertEqual + { actual: NESCU.lastIndexOf' (Pattern "bc") 2 (nes (SProxy :: SProxy "abcd")) + , expected: Just 1 + } + assertEqual + { actual: NESCU.lastIndexOf' (Pattern "cb") 0 (nes (SProxy :: SProxy "abcd")) + , expected: Nothing + } + + log "length" + assertEqual + { actual: NESCU.length (nes (SProxy :: SProxy "a")) + , expected: 1 + } + assertEqual + { actual: NESCU.length (nes (SProxy :: SProxy "ab")) + , expected: 2 + } + + log "take" + assertEqual + { actual: NESCU.take 0 (nes (SProxy :: SProxy "ab")) + , expected: Nothing + } + assertEqual + { actual: NESCU.take 1 (nes (SProxy :: SProxy "ab")) + , expected: Just (nes (SProxy :: SProxy "a")) + } + assertEqual + { actual: NESCU.take 2 (nes (SProxy :: SProxy "ab")) + , expected: Just (nes (SProxy :: SProxy "ab")) + } + assertEqual + { actual: NESCU.take 3 (nes (SProxy :: SProxy "ab")) + , expected: Just (nes (SProxy :: SProxy "ab")) + } + assertEqual + { actual: NESCU.take (-1) (nes (SProxy :: SProxy "ab")) + , expected: Nothing + } + + log "takeRight" + assertEqual + { actual: NESCU.takeRight 0 (nes (SProxy :: SProxy "ab")) + , expected: Nothing + } + assertEqual + { actual: NESCU.takeRight 1 (nes (SProxy :: SProxy "ab")) + , expected: Just (nes (SProxy :: SProxy "b")) + } + assertEqual + { actual: NESCU.takeRight 2 (nes (SProxy :: SProxy "ab")) + , expected: Just (nes (SProxy :: SProxy "ab")) + } + assertEqual + { actual: NESCU.takeRight 3 (nes (SProxy :: SProxy "ab")) + , expected: Just (nes (SProxy :: SProxy "ab")) + } + assertEqual + { actual: NESCU.takeRight (-1) (nes (SProxy :: SProxy "ab")) + , expected: Nothing + } + + log "drop" + assertEqual + { actual: NESCU.drop 0 (nes (SProxy :: SProxy "ab")) + , expected: Just (nes (SProxy :: SProxy "ab")) + } + assertEqual + { actual: NESCU.drop 1 (nes (SProxy :: SProxy "ab")) + , expected: Just (nes (SProxy :: SProxy "b")) + } + assertEqual + { actual: NESCU.drop 2 (nes (SProxy :: SProxy "ab")) + , expected: Nothing + } + assertEqual + { actual: NESCU.drop 3 (nes (SProxy :: SProxy "ab")) + , expected: Nothing + } + assertEqual + { actual: NESCU.drop (-1) (nes (SProxy :: SProxy "ab")) + , expected: Just (nes (SProxy :: SProxy "ab")) + } + + log "dropRight" + assertEqual + { actual: NESCU.dropRight 0 (nes (SProxy :: SProxy "ab")) + , expected: Just (nes (SProxy :: SProxy "ab")) + } + assertEqual + { actual: NESCU.dropRight 1 (nes (SProxy :: SProxy "ab")) + , expected: Just (nes (SProxy :: SProxy "a")) + } + assertEqual + { actual: NESCU.dropRight 2 (nes (SProxy :: SProxy "ab")) + , expected: Nothing + } + assertEqual + { actual: NESCU.dropRight 3 (nes (SProxy :: SProxy "ab")) + , expected: Nothing + } + assertEqual + { actual: NESCU.dropRight (-1) (nes (SProxy :: SProxy "ab")) + , expected: Just (nes (SProxy :: SProxy "ab")) + } + + log "countPrefix" + assertEqual + { actual: NESCU.countPrefix (_ == 'a') (nes (SProxy :: SProxy "ab")) + , expected: 1 + } + assertEqual + { actual: NESCU.countPrefix (_ == 'a') (nes (SProxy :: SProxy "aaab")) + , expected: 3 + } + assertEqual + { actual: NESCU.countPrefix (_ == 'a') (nes (SProxy :: SProxy "abaa")) + , expected: 1 + } + assertEqual + { actual: NESCU.countPrefix (_ == 'c') (nes (SProxy :: SProxy "abaa")) + , expected: 0 + } + + log "splitAt" + assertEqual + { actual: NESCU.splitAt 0 (nes (SProxy :: SProxy "a")) + , expected: { before: Nothing, after: Just (nes (SProxy :: SProxy "a")) } + } + assertEqual + { actual: NESCU.splitAt 1 (nes (SProxy :: SProxy "ab")) + , expected: { before: Just (nes (SProxy :: SProxy "a")), after: Just (nes (SProxy :: SProxy "b")) } + } + assertEqual + { actual: NESCU.splitAt 3 (nes (SProxy :: SProxy "aabcc")) + , expected: { before: Just (nes (SProxy :: SProxy "aab")), after: Just (nes (SProxy :: SProxy "cc")) } + } + assertEqual + { actual: NESCU.splitAt (-1) (nes (SProxy :: SProxy "abc")) + , expected: { before: Nothing, after: Just (nes (SProxy :: SProxy "abc")) } + } + +nea :: Array ~> NEA.NonEmptyArray +nea = unsafePartial fromJust <<< NEA.fromArray diff --git a/test/Test/Data/String/Unsafe.purs b/test/Test/Data/String/Unsafe.purs index c87055a..b6b9aca 100644 --- a/test/Test/Data/String/Unsafe.purs +++ b/test/Test/Data/String/Unsafe.purs @@ -1,23 +1,26 @@ module Test.Data.String.Unsafe (testStringUnsafe) where -import Prelude (Unit, (==), ($), discard) +import Prelude +import Data.String.Unsafe as SU import Effect (Effect) import Effect.Console (log) - -import Data.String.Unsafe - -import Test.Assert (assert) +import Test.Assert (assertEqual) testStringUnsafe :: Effect Unit 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' + assertEqual + { actual: SU.charAt 0 "ab" + , expected: 'a' + } + assertEqual + { actual: SU.charAt 1 "ab" + , expected: 'b' + } log "char" - assert $ char "a" == 'a' + assertEqual + { actual: SU.char "a" + , expected: 'a' + } diff --git a/test/Test/Main.purs b/test/Test/Main.purs index 92d6cd1..ad392d7 100644 --- a/test/Test/Main.purs +++ b/test/Test/Main.purs @@ -8,7 +8,9 @@ import Test.Data.Char (testChar) import Test.Data.String (testString) import Test.Data.String.CaseInsensitive (testCaseInsensitiveString) import Test.Data.String.CodePoints (testStringCodePoints) +import Test.Data.String.CodeUnits (testStringCodeUnits) import Test.Data.String.NonEmpty (testNonEmptyString) +import Test.Data.String.NonEmpty.CodeUnits (testNonEmptyStringCodeUnits) import Test.Data.String.Regex (testStringRegex) import Test.Data.String.Unsafe (testStringUnsafe) @@ -20,6 +22,8 @@ main = do testString log "\n--- Data.String.CodePoints ---\n" testStringCodePoints + log "\n--- Data.String.CodeUnits ---\n" + testStringCodeUnits log "\n--- Data.String.Unsafe ---\n" testStringUnsafe log "\n--- Data.String.Regex ---\n" @@ -28,3 +32,5 @@ main = do testCaseInsensitiveString log "\n--- Data.String.NonEmpty ---\n" testNonEmptyString + log "\n--- Data.String.NonEmpty.CodeUnits ---\n" + testNonEmptyStringCodeUnits From 765e1df50993e01c6526ab9bfd028df3074ae8c9 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Mon, 21 May 2018 23:11:05 +0100 Subject: [PATCH 144/186] Remove Char module - toLower/toUpper should return String anyway --- src/Data/Char.js | 9 --------- src/Data/Char.purs | 11 ----------- test/Test/Data/Char.purs | 31 ------------------------------- test/Test/Main.purs | 3 --- 4 files changed, 54 deletions(-) delete mode 100644 src/Data/Char.js delete mode 100644 src/Data/Char.purs delete mode 100644 test/Test/Data/Char.purs diff --git a/src/Data/Char.js b/src/Data/Char.js deleted file mode 100644 index d396533..0000000 --- a/src/Data/Char.js +++ /dev/null @@ -1,9 +0,0 @@ -"use strict"; - -exports.toLower = function (c) { - return c.toLowerCase(); -}; - -exports.toUpper = function (c) { - return c.toUpperCase(); -}; diff --git a/src/Data/Char.purs b/src/Data/Char.purs deleted file mode 100644 index 169abb0..0000000 --- a/src/Data/Char.purs +++ /dev/null @@ -1,11 +0,0 @@ --- | A type and functions for single characters. -module Data.Char - ( toLower - , toUpper - ) where - --- | Converts a character to lowercase. -foreign import toLower :: Char -> Char - --- | Converts a character to uppercase. -foreign import toUpper :: Char -> Char diff --git a/test/Test/Data/Char.purs b/test/Test/Data/Char.purs deleted file mode 100644 index f6d80ea..0000000 --- a/test/Test/Data/Char.purs +++ /dev/null @@ -1,31 +0,0 @@ -module Test.Data.Char (testChar) where - -import Prelude - -import Data.Char as C -import Effect (Effect) -import Effect.Console (log) -import Test.Assert (assertEqual) - -testChar :: Effect Unit -testChar = do - - log "toLower" - assertEqual - { actual: C.toLower 'A' - , expected: 'a' - } - assertEqual - { actual: C.toLower 'a' - , expected: 'a' - } - - log "toUpper" - assertEqual - { actual: C.toUpper 'a' - , expected: 'A' - } - assertEqual - { actual: C.toUpper 'A' - , expected: 'A' - } diff --git a/test/Test/Main.purs b/test/Test/Main.purs index ad392d7..fb9f32e 100644 --- a/test/Test/Main.purs +++ b/test/Test/Main.purs @@ -4,7 +4,6 @@ import Prelude import Effect (Effect) import Effect.Console (log) -import Test.Data.Char (testChar) import Test.Data.String (testString) import Test.Data.String.CaseInsensitive (testCaseInsensitiveString) import Test.Data.String.CodePoints (testStringCodePoints) @@ -16,8 +15,6 @@ import Test.Data.String.Unsafe (testStringUnsafe) main :: Effect Unit main = do - log "\n--- Data.Char ---\n" - testChar log "\n--- Data.String ---\n" testString log "\n--- Data.String.CodePoints ---\n" From 0903ec7efe220bb627beb052301b0545914d3c69 Mon Sep 17 00:00:00 2001 From: Christoph Date: Tue, 22 May 2018 16:10:05 +0200 Subject: [PATCH 145/186] resurrect toCharCode and fromCharCode --- src/Data/Char.purs | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/Data/Char.purs diff --git a/src/Data/Char.purs b/src/Data/Char.purs new file mode 100644 index 0000000..81c3fad --- /dev/null +++ b/src/Data/Char.purs @@ -0,0 +1,13 @@ +-- | A type and functions for single characters. +module Data.Char + ( toCharCode + , fromCharCode + ) where + +-- | Returns the numeric Unicode value of the character. +toCharCode :: Char -> Int +toCharCode = fromEnum + +-- | Constructs a character from the given Unicode numeric value. +fromCharCode :: Int -> Maybe Char +fromCharCode = toEnum From 6bbabd865a3af6d82ed7e955c232d43c6f41f104 Mon Sep 17 00:00:00 2001 From: Christoph Date: Tue, 22 May 2018 16:27:38 +0200 Subject: [PATCH 146/186] :facepalm: --- src/Data/Char.purs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Data/Char.purs b/src/Data/Char.purs index 81c3fad..bb413b7 100644 --- a/src/Data/Char.purs +++ b/src/Data/Char.purs @@ -4,6 +4,9 @@ module Data.Char , fromCharCode ) where +import Data.Enum (fromEnum, toEnum) +import Data.Maybe (Maybe) + -- | Returns the numeric Unicode value of the character. toCharCode :: Char -> Int toCharCode = fromEnum From 1fbc4c0cf0fb816870a6841fa83c5cbdcddaaf22 Mon Sep 17 00:00:00 2001 From: Christoph Date: Tue, 22 May 2018 17:59:44 +0200 Subject: [PATCH 147/186] reexport Data.String.CodePoints from Data.String --- src/Data/String.purs | 11 ++++++----- src/Data/String/CodePoints.purs | 9 +++++---- src/Data/String/CodeUnits.purs | 7 ++----- src/Data/String/Regex.purs | 3 ++- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Data/String.purs b/src/Data/String.purs index a382e6e..742f265 100644 --- a/src/Data/String.purs +++ b/src/Data/String.purs @@ -1,9 +1,10 @@ module Data.String - ( module Data.String.Pattern - , module Data.String.Common - , module Data.String.CodeUnits + ( module Data.String.Common + , module Data.String.CodePoints + , module Data.String.Pattern ) where -import Data.String.Pattern (Pattern(..), Replacement(..)) +import Data.String.CodePoints + import Data.String.Common (joinWith, localeCompare, null, replace, replaceAll, split, toLower, toUpper, trim) -import Data.String.CodeUnits (contains, stripPrefix, stripSuffix) +import Data.String.Pattern (Pattern(..), Replacement(..)) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 473731c..c9b5b15 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -4,7 +4,7 @@ -- | strings, these functions should be preferred over the ones in -- | `Data.String.CodeUnits`. module Data.String.CodePoints - ( module Data.String + ( module Exports , CodePoint , codePointFromChar , singleton @@ -34,9 +34,10 @@ import Data.Array as Array import Data.Enum (class BoundedEnum, class Enum, Cardinality(..), defaultPred, defaultSucc, fromEnum, toEnum, toEnumWithDefaults) import Data.Int (hexadecimal, toStringAs) import Data.Maybe (Maybe(..)) -import Data.String as String +import Data.String.CodeUnits (contains, stripPrefix, stripSuffix) as Exports import Data.String.CodeUnits as CU -import Data.String (Pattern(..), Replacement(..), contains, joinWith, localeCompare, null, replace, replaceAll, split, stripPrefix, stripSuffix, toLower, toUpper, trim) +import Data.String.Common (toUpper) +import Data.String.Pattern (Pattern) import Data.String.Unsafe as Unsafe import Data.Tuple (Tuple(..)) import Data.Unfoldable (unfoldr) @@ -49,7 +50,7 @@ derive instance eqCodePoint :: Eq CodePoint derive instance ordCodePoint :: Ord CodePoint instance showCodePoint :: Show CodePoint where - show (CodePoint i) = "(CodePoint 0x" <> String.toUpper (toStringAs hexadecimal i) <> ")" + show (CodePoint i) = "(CodePoint 0x" <> toUpper (toStringAs hexadecimal i) <> ")" instance boundedCodePoint :: Bounded CodePoint where bottom = CodePoint 0 diff --git a/src/Data/String/CodeUnits.purs b/src/Data/String/CodeUnits.purs index b67a639..1b127ef 100644 --- a/src/Data/String/CodeUnits.purs +++ b/src/Data/String/CodeUnits.purs @@ -1,7 +1,5 @@ module Data.String.CodeUnits - ( module Data.String.Pattern - , module Data.String.Common - , stripPrefix + ( stripPrefix , stripSuffix , contains , singleton @@ -29,8 +27,7 @@ module Data.String.CodeUnits import Prelude import Data.Maybe (Maybe(..), isJust) -import Data.String.Common (joinWith, localeCompare, null, replace, replaceAll, split, toLower, toUpper, trim) -import Data.String.Pattern (Pattern(..), Replacement(..)) +import Data.String.Pattern (Pattern(..)) import Data.String.Unsafe as U ------------------------------------------------------------------------------- diff --git a/src/Data/String/Regex.purs b/src/Data/String/Regex.purs index 79a1a9f..d42145a 100644 --- a/src/Data/String/Regex.purs +++ b/src/Data/String/Regex.purs @@ -21,7 +21,8 @@ import Prelude import Data.Array.NonEmpty (NonEmptyArray) import Data.Either (Either(..)) import Data.Maybe (Maybe(..)) -import Data.String (Pattern(..), contains) +import Data.String (contains) +import Data.String.Pattern (Pattern(..)) import Data.String.Regex.Flags (RegexFlags(..), RegexFlagsRec) -- | Wraps Javascript `RegExp` objects. From cdfef73597c9f0c4ed8e157d511d40e6e120ca12 Mon Sep 17 00:00:00 2001 From: Christoph Date: Tue, 22 May 2018 23:18:44 +0200 Subject: [PATCH 148/186] mirror .String structure from .NonEmpty --- src/Data/String/NonEmpty.purs | 242 +---------------------- src/Data/String/NonEmpty/CodePoints.purs | 6 +- src/Data/String/NonEmpty/CodeUnits.purs | 6 +- src/Data/String/NonEmpty/Internal.purs | 219 ++++++++++++++++++++ 4 files changed, 230 insertions(+), 243 deletions(-) create mode 100644 src/Data/String/NonEmpty/Internal.purs diff --git a/src/Data/String/NonEmpty.purs b/src/Data/String/NonEmpty.purs index 02988a1..6b6210c 100644 --- a/src/Data/String/NonEmpty.purs +++ b/src/Data/String/NonEmpty.purs @@ -1,241 +1,9 @@ module Data.String.NonEmpty - ( NonEmptyString - , class MakeNonEmpty, nes - , NonEmptyReplacement(..) - , fromString - , unsafeFromString - , toString - , appendString - , prependString - , contains - , localeCompare - , replace - , replaceAll - , stripPrefix - , stripSuffix - , toLower - , toUpper - , trim - , joinWith - , join1With - , joinWith1 - , module Data.String.Pattern + ( module Data.String.Pattern + , module Data.String.NonEmpty.Internal + , module Data.String.NonEmpty.CodePoints ) where -import Prelude - -import Data.Foldable (class Foldable) -import Data.Foldable as F -import Data.Maybe (Maybe(..), fromJust) -import Data.Semigroup.Foldable (class Foldable1) -import Data.String as String +import Data.String.NonEmpty.Internal (NonEmptyString, class MakeNonEmpty, NonEmptyReplacement(..), appendString, contains, fromString, join1With, joinWith, joinWith1, localeCompare, nes, prependString, replace, replaceAll, stripPrefix, stripSuffix, toLower, toString, toUpper, trim, unsafeFromString) import Data.String.Pattern (Pattern(..)) -import Data.Symbol (class IsSymbol, SProxy, reflectSymbol) -import Prim.TypeError as TE -import Unsafe.Coerce (unsafeCoerce) - --- | A string that is known not to be empty. -newtype NonEmptyString = NonEmptyString String - -derive newtype instance eqNonEmptyString ∷ Eq NonEmptyString -derive newtype instance ordNonEmptyString ∷ Ord NonEmptyString -derive newtype instance semigroupNonEmptyString ∷ Semigroup NonEmptyString - -instance showNonEmptyString :: Show NonEmptyString where - show (NonEmptyString s) = "(NonEmptyString.unsafeFromString " <> show s <> ")" - --- | A helper class for defining non-empty string values at compile time. --- | --- | ``` purescript --- | something :: NonEmptyString --- | something = nes (SProxy :: SProxy "something") --- | ``` -class MakeNonEmpty (s :: Symbol) where - nes :: SProxy s -> NonEmptyString - -instance makeNonEmptyBad :: TE.Fail (TE.Text "Cannot create an NonEmptyString from an empty Symbol") => MakeNonEmpty "" where - nes _ = NonEmptyString "" - -else instance nonEmptyNonEmpty :: IsSymbol s => MakeNonEmpty s where - nes p = NonEmptyString (reflectSymbol p) - --- | A newtype used in cases to specify a non-empty replacement for a pattern. -newtype NonEmptyReplacement = NonEmptyReplacement NonEmptyString - -derive newtype instance eqNonEmptyReplacement :: Eq NonEmptyReplacement -derive newtype instance ordNonEmptyReplacement :: Ord NonEmptyReplacement -derive newtype instance semigroupNonEmptyReplacement ∷ Semigroup NonEmptyReplacement - -instance showNonEmptyReplacement :: Show NonEmptyReplacement where - show (NonEmptyReplacement s) = "(NonEmptyReplacement " <> show s <> ")" - --- | Creates a `NonEmptyString` from a `String`, returning `Nothing` if the --- | input is empty. --- | --- | ```purescript --- | fromString "" = Nothing --- | fromString "hello" = Just (NES.unsafeFromString "hello") --- | ``` -fromString :: String -> Maybe NonEmptyString -fromString = case _ of - "" -> Nothing - s -> Just (NonEmptyString s) - --- | A partial version of `fromString`. -unsafeFromString :: Partial => String -> NonEmptyString -unsafeFromString = fromJust <<< fromString - --- | Converts a `NonEmptyString` back into a standard `String`. -toString :: NonEmptyString -> String -toString (NonEmptyString s) = s - --- | Appends a string to this non-empty string. Since one of the strings is --- | non-empty we know the result will be too. --- | --- | ```purescript --- | appendString (NonEmptyString "Hello") " world" == NonEmptyString "Hello world" --- | appendString (NonEmptyString "Hello") "" == NonEmptyString "Hello" --- | ``` -appendString :: NonEmptyString -> String -> NonEmptyString -appendString (NonEmptyString s1) s2 = NonEmptyString (s1 <> s2) - --- | Prepends a string to this non-empty string. Since one of the strings is --- | non-empty we know the result will be too. --- | --- | ```purescript --- | prependString "be" (NonEmptyString "fore") == NonEmptyString "before" --- | prependString "" (NonEmptyString "fore") == NonEmptyString "fore" --- | ``` -prependString :: String -> NonEmptyString -> NonEmptyString -prependString s1 (NonEmptyString s2) = NonEmptyString (s1 <> s2) - --- | If the string starts with the given prefix, return the portion of the --- | string left after removing it. If the prefix does not match or there is no --- | remainder, the result will be `Nothing`. --- | --- | ```purescript --- | stripPrefix (Pattern "http:") (NonEmptyString "http://purescript.org") == Just (NonEmptyString "//purescript.org") --- | stripPrefix (Pattern "http:") (NonEmptyString "https://purescript.org") == Nothing --- | stripPrefix (Pattern "Hello!") (NonEmptyString "Hello!") == Nothing --- | ``` -stripPrefix :: Pattern -> NonEmptyString -> Maybe NonEmptyString -stripPrefix pat = fromString <=< liftS (String.stripPrefix pat) - --- | If the string ends with the given suffix, return the portion of the --- | string left after removing it. If the suffix does not match or there is no --- | remainder, the result will be `Nothing`. --- | --- | ```purescript --- | stripSuffix (Pattern ".exe") (NonEmptyString "purs.exe") == Just (NonEmptyString "purs") --- | stripSuffix (Pattern ".exe") (NonEmptyString "purs") == Nothing --- | stripSuffix (Pattern "Hello!") (NonEmptyString "Hello!") == Nothing --- | ``` -stripSuffix :: Pattern -> NonEmptyString -> Maybe NonEmptyString -stripSuffix pat = fromString <=< liftS (String.stripSuffix pat) - --- | Checks whether the pattern appears in the given string. --- | --- | ```purescript --- | contains (Pattern "needle") (NonEmptyString "haystack with needle") == true --- | contains (Pattern "needle") (NonEmptyString "haystack") == false --- | ``` -contains :: Pattern -> NonEmptyString -> Boolean -contains = liftS <<< String.contains - --- | Compare two strings in a locale-aware fashion. This is in contrast to --- | the `Ord` instance on `String` which treats strings as arrays of code --- | units: --- | --- | ```purescript --- | NonEmptyString "Γ€" `localeCompare` NonEmptyString "b" == LT --- | NonEmptyString "Γ€" `compare` NonEmptyString "b" == GT --- | ``` -localeCompare :: NonEmptyString -> NonEmptyString -> Ordering -localeCompare (NonEmptyString a) (NonEmptyString b) = String.localeCompare a b - --- | Replaces the first occurence of the pattern with the replacement string. --- | --- | ```purescript --- | replace (Pattern "<=") (NonEmptyReplacement "≀") (NonEmptyString "a <= b <= c") == NonEmptyString "a ≀ b <= c" --- | ``` -replace :: Pattern -> NonEmptyReplacement -> NonEmptyString -> NonEmptyString -replace pat (NonEmptyReplacement (NonEmptyString rep)) (NonEmptyString s) = - NonEmptyString (String.replace pat (String.Replacement rep) s) - --- | Replaces all occurences of the pattern with the replacement string. --- | --- | ```purescript --- | replaceAll (Pattern "<=") (NonEmptyReplacement "≀") (NonEmptyString "a <= b <= c") == NonEmptyString "a ≀ b ≀ c" --- | ``` -replaceAll :: Pattern -> NonEmptyReplacement -> NonEmptyString -> NonEmptyString -replaceAll pat (NonEmptyReplacement (NonEmptyString rep)) (NonEmptyString s) = - NonEmptyString (String.replaceAll pat (String.Replacement rep) s) - --- | Returns the argument converted to lowercase. --- | --- | ```purescript --- | toLower (NonEmptyString "hElLo") == NonEmptyString "hello" --- | ``` -toLower :: NonEmptyString -> NonEmptyString -toLower (NonEmptyString s) = NonEmptyString (String.toLower s) - --- | Returns the argument converted to uppercase. --- | --- | ```purescript --- | toUpper (NonEmptyString "Hello") == NonEmptyString "HELLO" --- | ``` -toUpper :: NonEmptyString -> NonEmptyString -toUpper (NonEmptyString s) = NonEmptyString (String.toUpper s) - --- | Removes whitespace from the beginning and end of a string, including --- | [whitespace characters](http://www.ecma-international.org/ecma-262/5.1/#sec-7.2) --- | and [line terminators](http://www.ecma-international.org/ecma-262/5.1/#sec-7.3). --- | If the string is entirely made up of whitespace the result will be Nothing. --- | --- | ```purescript --- | trim (NonEmptyString " Hello \n World\n\t ") == Just (NonEmptyString "Hello \n World") --- | trim (NonEmptyString " \n") == Nothing --- | ``` -trim :: NonEmptyString -> Maybe NonEmptyString -trim (NonEmptyString s) = fromString (String.trim s) - --- | Joins the strings in a container together as a new string, inserting the --- | first argument as separator between them. The result is not guaranteed to --- | be non-empty. --- | --- | ```purescript --- | joinWith ", " [NonEmptyString "apple", NonEmptyString "banana"] == "apple, banana" --- | joinWith ", " [] == "" --- | ``` -joinWith :: forall f. Foldable f => String -> f NonEmptyString -> String -joinWith splice = F.intercalate splice <<< coe - where - coe :: f NonEmptyString -> f String - coe = unsafeCoerce - --- | Joins non-empty strings in a non-empty container together as a new --- | non-empty string, inserting a possibly empty string as separator between --- | them. The result is guaranteed to be non-empty. --- | --- | ```purescript --- | -- array syntax is used for demonstration here, it would need to be a real `Foldable1` --- | join1With ", " [NonEmptyString "apple", NonEmptyString "banana"] == NonEmptyString "apple, banana" --- | join1With "" [NonEmptyString "apple", NonEmptyString "banana"] == NonEmptyString "applebanana" --- | ``` -join1With :: forall f. Foldable1 f => String -> f NonEmptyString -> NonEmptyString -join1With splice = NonEmptyString <<< joinWith splice - --- | Joins possibly empty strings in a non-empty container together as a new --- | non-empty string, inserting a non-empty string as a separator between them. --- | The result is guaranteed to be non-empty. --- | --- | ```purescript --- | -- array syntax is used for demonstration here, it would need to be a real `Foldable1` --- | joinWith1 (NonEmptyString ", ") ["apple", "banana"] == NonEmptyString "apple, banana" --- | joinWith1 (NonEmptyString "/") ["a", "b", "", "c", ""] == NonEmptyString "a/b//c/" --- | ``` -joinWith1 :: forall f. Foldable1 f => NonEmptyString -> f String -> NonEmptyString -joinWith1 (NonEmptyString splice) = NonEmptyString <<< F.intercalate splice - -liftS :: forall r. (String -> r) -> NonEmptyString -> r -liftS f (NonEmptyString s) = f s +import Data.String.NonEmpty.CodePoints diff --git a/src/Data/String/NonEmpty/CodePoints.purs b/src/Data/String/NonEmpty/CodePoints.purs index 5ec1254..5357851 100644 --- a/src/Data/String/NonEmpty/CodePoints.purs +++ b/src/Data/String/NonEmpty/CodePoints.purs @@ -1,6 +1,5 @@ module Data.String.NonEmpty.CodePoints - ( module Data.String.NonEmpty - , fromCodePointArray + ( fromCodePointArray , fromNonEmptyCodePointArray , singleton , cons @@ -34,7 +33,8 @@ import Data.Semigroup.Foldable (class Foldable1) import Data.Semigroup.Foldable as F1 import Data.String.CodePoints (CodePoint) import Data.String.CodePoints as CP -import Data.String.NonEmpty (class MakeNonEmpty, NonEmptyReplacement(..), NonEmptyString, Pattern(..), appendString, contains, fromString, join1With, joinWith, joinWith1, localeCompare, nes, prependString, replace, replaceAll, stripPrefix, stripSuffix, toLower, toString, toUpper, trim, unsafeFromString) +import Data.String.NonEmpty.Internal (NonEmptyString, fromString) +import Data.String.Pattern (Pattern) import Partial.Unsafe (unsafePartial) import Unsafe.Coerce (unsafeCoerce) diff --git a/src/Data/String/NonEmpty/CodeUnits.purs b/src/Data/String/NonEmpty/CodeUnits.purs index 0a826f0..2aa0a6c 100644 --- a/src/Data/String/NonEmpty/CodeUnits.purs +++ b/src/Data/String/NonEmpty/CodeUnits.purs @@ -1,6 +1,5 @@ module Data.String.NonEmpty.CodeUnits - ( module Data.String.NonEmpty - , fromCharArray + ( fromCharArray , fromNonEmptyCharArray , singleton , cons @@ -34,8 +33,9 @@ import Data.Maybe (Maybe(..), fromJust) import Data.Semigroup.Foldable (class Foldable1) import Data.Semigroup.Foldable as F1 import Data.String.CodeUnits as CU +import Data.String.NonEmpty.Internal (NonEmptyString, fromString) +import Data.String.Pattern (Pattern) import Data.String.Unsafe as U -import Data.String.NonEmpty (class MakeNonEmpty, NonEmptyReplacement(..), NonEmptyString, Pattern(..), appendString, contains, fromString, join1With, joinWith, joinWith1, localeCompare, nes, prependString, replace, replaceAll, stripPrefix, stripSuffix, toLower, toString, toUpper, trim, unsafeFromString) import Partial.Unsafe (unsafePartial) import Unsafe.Coerce (unsafeCoerce) diff --git a/src/Data/String/NonEmpty/Internal.purs b/src/Data/String/NonEmpty/Internal.purs new file mode 100644 index 0000000..bfd2984 --- /dev/null +++ b/src/Data/String/NonEmpty/Internal.purs @@ -0,0 +1,219 @@ +module Data.String.NonEmpty.Internal where + +import Prelude + +import Data.Foldable (class Foldable) +import Data.Foldable as F +import Data.Maybe (Maybe(..), fromJust) +import Data.Semigroup.Foldable (class Foldable1) +import Data.String as String +import Data.String.Pattern (Pattern) +import Data.Symbol (class IsSymbol, SProxy, reflectSymbol) +import Prim.TypeError as TE +import Unsafe.Coerce (unsafeCoerce) + +-- | A string that is known not to be empty. +newtype NonEmptyString = NonEmptyString String + +derive newtype instance eqNonEmptyString ∷ Eq NonEmptyString +derive newtype instance ordNonEmptyString ∷ Ord NonEmptyString +derive newtype instance semigroupNonEmptyString ∷ Semigroup NonEmptyString + +instance showNonEmptyString :: Show NonEmptyString where + show (NonEmptyString s) = "(NonEmptyString.unsafeFromString " <> show s <> ")" + +-- | A helper class for defining non-empty string values at compile time. +-- | +-- | ``` purescript +-- | something :: NonEmptyString +-- | something = nes (SProxy :: SProxy "something") +-- | ``` +class MakeNonEmpty (s :: Symbol) where + nes :: SProxy s -> NonEmptyString + +instance makeNonEmptyBad :: TE.Fail (TE.Text "Cannot create an NonEmptyString from an empty Symbol") => MakeNonEmpty "" where + nes _ = NonEmptyString "" + +else instance nonEmptyNonEmpty :: IsSymbol s => MakeNonEmpty s where + nes p = NonEmptyString (reflectSymbol p) + +-- | A newtype used in cases to specify a non-empty replacement for a pattern. +newtype NonEmptyReplacement = NonEmptyReplacement NonEmptyString + +derive newtype instance eqNonEmptyReplacement :: Eq NonEmptyReplacement +derive newtype instance ordNonEmptyReplacement :: Ord NonEmptyReplacement +derive newtype instance semigroupNonEmptyReplacement ∷ Semigroup NonEmptyReplacement + +instance showNonEmptyReplacement :: Show NonEmptyReplacement where + show (NonEmptyReplacement s) = "(NonEmptyReplacement " <> show s <> ")" + +-- | Creates a `NonEmptyString` from a `String`, returning `Nothing` if the +-- | input is empty. +-- | +-- | ```purescript +-- | fromString "" = Nothing +-- | fromString "hello" = Just (NES.unsafeFromString "hello") +-- | ``` +fromString :: String -> Maybe NonEmptyString +fromString = case _ of + "" -> Nothing + s -> Just (NonEmptyString s) + +-- | A partial version of `fromString`. +unsafeFromString :: Partial => String -> NonEmptyString +unsafeFromString = fromJust <<< fromString + +-- | Converts a `NonEmptyString` back into a standard `String`. +toString :: NonEmptyString -> String +toString (NonEmptyString s) = s + +-- | Appends a string to this non-empty string. Since one of the strings is +-- | non-empty we know the result will be too. +-- | +-- | ```purescript +-- | appendString (NonEmptyString "Hello") " world" == NonEmptyString "Hello world" +-- | appendString (NonEmptyString "Hello") "" == NonEmptyString "Hello" +-- | ``` +appendString :: NonEmptyString -> String -> NonEmptyString +appendString (NonEmptyString s1) s2 = NonEmptyString (s1 <> s2) + +-- | Prepends a string to this non-empty string. Since one of the strings is +-- | non-empty we know the result will be too. +-- | +-- | ```purescript +-- | prependString "be" (NonEmptyString "fore") == NonEmptyString "before" +-- | prependString "" (NonEmptyString "fore") == NonEmptyString "fore" +-- | ``` +prependString :: String -> NonEmptyString -> NonEmptyString +prependString s1 (NonEmptyString s2) = NonEmptyString (s1 <> s2) + +-- | If the string starts with the given prefix, return the portion of the +-- | string left after removing it. If the prefix does not match or there is no +-- | remainder, the result will be `Nothing`. +-- | +-- | ```purescript +-- | stripPrefix (Pattern "http:") (NonEmptyString "http://purescript.org") == Just (NonEmptyString "//purescript.org") +-- | stripPrefix (Pattern "http:") (NonEmptyString "https://purescript.org") == Nothing +-- | stripPrefix (Pattern "Hello!") (NonEmptyString "Hello!") == Nothing +-- | ``` +stripPrefix :: Pattern -> NonEmptyString -> Maybe NonEmptyString +stripPrefix pat = fromString <=< liftS (String.stripPrefix pat) + +-- | If the string ends with the given suffix, return the portion of the +-- | string left after removing it. If the suffix does not match or there is no +-- | remainder, the result will be `Nothing`. +-- | +-- | ```purescript +-- | stripSuffix (Pattern ".exe") (NonEmptyString "purs.exe") == Just (NonEmptyString "purs") +-- | stripSuffix (Pattern ".exe") (NonEmptyString "purs") == Nothing +-- | stripSuffix (Pattern "Hello!") (NonEmptyString "Hello!") == Nothing +-- | ``` +stripSuffix :: Pattern -> NonEmptyString -> Maybe NonEmptyString +stripSuffix pat = fromString <=< liftS (String.stripSuffix pat) + +-- | Checks whether the pattern appears in the given string. +-- | +-- | ```purescript +-- | contains (Pattern "needle") (NonEmptyString "haystack with needle") == true +-- | contains (Pattern "needle") (NonEmptyString "haystack") == false +-- | ``` +contains :: Pattern -> NonEmptyString -> Boolean +contains = liftS <<< String.contains + +-- | Compare two strings in a locale-aware fashion. This is in contrast to +-- | the `Ord` instance on `String` which treats strings as arrays of code +-- | units: +-- | +-- | ```purescript +-- | NonEmptyString "Γ€" `localeCompare` NonEmptyString "b" == LT +-- | NonEmptyString "Γ€" `compare` NonEmptyString "b" == GT +-- | ``` +localeCompare :: NonEmptyString -> NonEmptyString -> Ordering +localeCompare (NonEmptyString a) (NonEmptyString b) = String.localeCompare a b + +-- | Replaces the first occurence of the pattern with the replacement string. +-- | +-- | ```purescript +-- | replace (Pattern "<=") (NonEmptyReplacement "≀") (NonEmptyString "a <= b <= c") == NonEmptyString "a ≀ b <= c" +-- | ``` +replace :: Pattern -> NonEmptyReplacement -> NonEmptyString -> NonEmptyString +replace pat (NonEmptyReplacement (NonEmptyString rep)) (NonEmptyString s) = + NonEmptyString (String.replace pat (String.Replacement rep) s) + +-- | Replaces all occurences of the pattern with the replacement string. +-- | +-- | ```purescript +-- | replaceAll (Pattern "<=") (NonEmptyReplacement "≀") (NonEmptyString "a <= b <= c") == NonEmptyString "a ≀ b ≀ c" +-- | ``` +replaceAll :: Pattern -> NonEmptyReplacement -> NonEmptyString -> NonEmptyString +replaceAll pat (NonEmptyReplacement (NonEmptyString rep)) (NonEmptyString s) = + NonEmptyString (String.replaceAll pat (String.Replacement rep) s) + +-- | Returns the argument converted to lowercase. +-- | +-- | ```purescript +-- | toLower (NonEmptyString "hElLo") == NonEmptyString "hello" +-- | ``` +toLower :: NonEmptyString -> NonEmptyString +toLower (NonEmptyString s) = NonEmptyString (String.toLower s) + +-- | Returns the argument converted to uppercase. +-- | +-- | ```purescript +-- | toUpper (NonEmptyString "Hello") == NonEmptyString "HELLO" +-- | ``` +toUpper :: NonEmptyString -> NonEmptyString +toUpper (NonEmptyString s) = NonEmptyString (String.toUpper s) + +-- | Removes whitespace from the beginning and end of a string, including +-- | [whitespace characters](http://www.ecma-international.org/ecma-262/5.1/#sec-7.2) +-- | and [line terminators](http://www.ecma-international.org/ecma-262/5.1/#sec-7.3). +-- | If the string is entirely made up of whitespace the result will be Nothing. +-- | +-- | ```purescript +-- | trim (NonEmptyString " Hello \n World\n\t ") == Just (NonEmptyString "Hello \n World") +-- | trim (NonEmptyString " \n") == Nothing +-- | ``` +trim :: NonEmptyString -> Maybe NonEmptyString +trim (NonEmptyString s) = fromString (String.trim s) + +-- | Joins the strings in a container together as a new string, inserting the +-- | first argument as separator between them. The result is not guaranteed to +-- | be non-empty. +-- | +-- | ```purescript +-- | joinWith ", " [NonEmptyString "apple", NonEmptyString "banana"] == "apple, banana" +-- | joinWith ", " [] == "" +-- | ``` +joinWith :: forall f. Foldable f => String -> f NonEmptyString -> String +joinWith splice = F.intercalate splice <<< coe + where + coe :: f NonEmptyString -> f String + coe = unsafeCoerce + +-- | Joins non-empty strings in a non-empty container together as a new +-- | non-empty string, inserting a possibly empty string as separator between +-- | them. The result is guaranteed to be non-empty. +-- | +-- | ```purescript +-- | -- array syntax is used for demonstration here, it would need to be a real `Foldable1` +-- | join1With ", " [NonEmptyString "apple", NonEmptyString "banana"] == NonEmptyString "apple, banana" +-- | join1With "" [NonEmptyString "apple", NonEmptyString "banana"] == NonEmptyString "applebanana" +-- | ``` +join1With :: forall f. Foldable1 f => String -> f NonEmptyString -> NonEmptyString +join1With splice = NonEmptyString <<< joinWith splice + +-- | Joins possibly empty strings in a non-empty container together as a new +-- | non-empty string, inserting a non-empty string as a separator between them. +-- | The result is guaranteed to be non-empty. +-- | +-- | ```purescript +-- | -- array syntax is used for demonstration here, it would need to be a real `Foldable1` +-- | joinWith1 (NonEmptyString ", ") ["apple", "banana"] == NonEmptyString "apple, banana" +-- | joinWith1 (NonEmptyString "/") ["a", "b", "", "c", ""] == NonEmptyString "a/b//c/" +-- | ``` +joinWith1 :: forall f. Foldable1 f => NonEmptyString -> f String -> NonEmptyString +joinWith1 (NonEmptyString splice) = NonEmptyString <<< F.intercalate splice + +liftS :: forall r. (String -> r) -> NonEmptyString -> r +liftS f (NonEmptyString s) = f s From b1d444667ee494019082cf25f2584e8e2144f962 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Wed, 23 May 2018 23:53:31 +0100 Subject: [PATCH 149/186] Update dependencies, license, fix esling warning --- LICENSE | 38 ++++++++++++++++++++++---------------- bower.json | 33 ++++++++++++++++++++------------- package.json | 8 ++++---- src/Data/String/Common.js | 2 +- 4 files changed, 47 insertions(+), 34 deletions(-) diff --git a/LICENSE b/LICENSE index 58b0299..311379c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,20 +1,26 @@ -The MIT License (MIT) +Copyright 2018 PureScript -Copyright (c) 2014 PureScript +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/bower.json b/bower.json index db0c9b5..ec7fb7f 100644 --- a/bower.json +++ b/bower.json @@ -1,8 +1,7 @@ { "name": "purescript-strings", "homepage": "https://github.com/purescript/purescript-strings", - "description": "String and char utility functions, regular expressions.", - "license": "MIT", + "license": "BSD-3-Clause", "repository": { "type": "git", "url": "git://github.com/purescript/purescript-strings.git" @@ -17,18 +16,26 @@ "package.json" ], "dependencies": { - "purescript-arrays": "#compiler/0.12", - "purescript-either": "#compiler/0.12", - "purescript-enums": "#compiler/0.12", - "purescript-gen": "#compiler/0.12", - "purescript-integers": "#compiler/0.12", - "purescript-maybe": "#compiler/0.12", - "purescript-partial": "#compiler/0.12", - "purescript-unfoldable": "#compiler/0.12" + "purescript-arrays": "^5.0.0", + "purescript-control": "^4.0.0", + "purescript-either": "^4.0.0", + "purescript-enums": "^4.0.0", + "purescript-foldable-traversable": "^4.0.0", + "purescript-gen": "^2.0.0", + "purescript-integers": "^4.0.0", + "purescript-maybe": "^4.0.0", + "purescript-newtype": "^3.0.0", + "purescript-nonempty": "^5.0.0", + "purescript-partial": "^2.0.0", + "purescript-prelude": "^4.0.0", + "purescript-tailrec": "^4.0.0", + "purescript-tuples": "^5.0.0", + "purescript-unfoldable": "^4.0.0", + "purescript-unsafe-coerce": "^4.0.0" }, "devDependencies": { - "purescript-assert": "#compiler/0.12", - "purescript-console": "#compiler/0.12", - "purescript-minibench": "#compiler/0.12" + "purescript-assert": "^4.0.0", + "purescript-console": "^4.0.0", + "purescript-minibench": "^2.0.0" } } diff --git a/package.json b/package.json index 64f4ac9..d44d01e 100644 --- a/package.json +++ b/package.json @@ -9,9 +9,9 @@ "bench": "npm run bench:build && npm run bench:run" }, "devDependencies": { - "eslint": "^3.17.1", - "pulp": "^10.0.4", - "purescript-psa": "^0.5.0-rc.1", - "rimraf": "^2.6.1" + "eslint": "^4.19.1", + "pulp": "^12.2.0", + "purescript-psa": "^0.6.0", + "rimraf": "^2.6.2" } } diff --git a/src/Data/String/Common.js b/src/Data/String/Common.js index 3237741..111c02e 100644 --- a/src/Data/String/Common.js +++ b/src/Data/String/Common.js @@ -24,7 +24,7 @@ exports.replace = function (s1) { exports.replaceAll = function (s1) { return function (s2) { return function (s3) { - return s3.replace(new RegExp(s1.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&"), "g"), s2); + return s3.replace(new RegExp(s1.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&"), "g"), s2); // eslint-disable-line no-useless-escape }; }; }; From d94d97efc5b77444fcd0d565577f7d066d453f33 Mon Sep 17 00:00:00 2001 From: bouzuya Date: Thu, 4 Oct 2018 18:54:37 +0900 Subject: [PATCH 150/186] Fix splitAt example --- src/Data/String/CodePoints.purs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index c9b5b15..7f72b7b 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -369,7 +369,7 @@ dropWhile p s = drop (countPrefix p s) s -- | -- | ```purescript -- | >>> splitAt 3 "b 𝐀𝐀 c 𝐀" --- | Just { before: "b 𝐀", after: "𝐀 c 𝐀" } +-- | { before: "b 𝐀", after: "𝐀 c 𝐀" } -- | ``` -- | -- | Thus the length of `(splitAt i s).before` will equal either `i` or From 9d4387e685cfd1b285080785df027976ec6e2e2e Mon Sep 17 00:00:00 2001 From: Maciej Bielecki Date: Tue, 6 Nov 2018 12:30:06 +0100 Subject: [PATCH 151/186] Run tests without codePointAt to exercise fallbacks --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index d44d01e..28a3fd3 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,8 @@ "scripts": { "clean": "rimraf output && rimraf .pulp-cache", "build": "eslint src && pulp build -- --censor-lib --strict", - "test": "pulp test", + "test": "pulp test && npm run test:run:without_codePointAt", + "test:run:without_codePointAt": "node -e \"delete String.prototype.codePointAt; require('./output/Test.Main/index.js').main();\"", "bench:build": "purs compile 'bench/**/*.purs' 'src/**/*.purs' 'bower_components/*/src/**/*.purs'", "bench:run": "node --expose-gc -e 'require(\"./output/Bench.Main/index.js\").main()'", "bench": "npm run bench:build && npm run bench:run" From 45be85e59f5aea683633160f0484c35f114bade3 Mon Sep 17 00:00:00 2001 From: Maciej Bielecki Date: Tue, 6 Nov 2018 12:34:30 +0100 Subject: [PATCH 152/186] Fix out-out-bounds access in unsafeCodePointAt0Fallback `unsafeCodePointAt0Fallback` crashed when passed a string of length 1 due to always accessing index 1. --- src/Data/String/CodePoints.purs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 7f72b7b..8492087 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -415,8 +415,10 @@ unsafeCodePointAt0Fallback :: String -> CodePoint unsafeCodePointAt0Fallback s = let cu0 = fromEnum (Unsafe.charAt 0 s) - cu1 = fromEnum (Unsafe.charAt 1 s) in - if isLead cu0 && isTrail cu1 - then unsurrogate cu0 cu1 - else CodePoint cu0 + if isLead cu0 && CU.length s > 1 + then + let cu1 = fromEnum (Unsafe.charAt 1 s) in + if isTrail cu1 then unsurrogate cu0 cu1 else CodePoint cu0 + else + CodePoint cu0 From 6a74d00cc42299d2543ed869459503470959cb3c Mon Sep 17 00:00:00 2001 From: Dario Aprea Date: Fri, 9 Nov 2018 21:35:24 +0100 Subject: [PATCH 153/186] Fix slice when end index == string length --- src/Data/String/CodeUnits.purs | 2 +- test/Test/Data/String/CodeUnits.purs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Data/String/CodeUnits.purs b/src/Data/String/CodeUnits.purs index 1b127ef..753a0c0 100644 --- a/src/Data/String/CodeUnits.purs +++ b/src/Data/String/CodeUnits.purs @@ -305,7 +305,7 @@ dropWhile p s = drop (countPrefix p s) s -- | ``` slice :: Int -> Int -> String -> Maybe String slice b e s = if b' < 0 || b' >= l || - e' < 0 || e' >= l || + e' < 0 || e' > l || b' > e' then Nothing else Just (_slice b e s) diff --git a/test/Test/Data/String/CodeUnits.purs b/test/Test/Data/String/CodeUnits.purs index eb35d15..8106ba8 100644 --- a/test/Test/Data/String/CodeUnits.purs +++ b/test/Test/Data/String/CodeUnits.purs @@ -430,6 +430,10 @@ testStringCodeUnits = do { actual: SCU.slice 3 6 "purescript" , expected: Just "esc" } + assertEqual + { actual: SCU.slice 3 10 "purescript" + , expected: Just "escript" + } assertEqual { actual: SCU.slice (-4) (-1) "purescript" , expected: Just "rip" From aa5b16163b4974f0ad64f223dc6b3f0be8f743a8 Mon Sep 17 00:00:00 2001 From: menelaos Date: Sun, 21 Apr 2019 16:35:10 +0200 Subject: [PATCH 154/186] Fix documentation for `singleton` (#115) --- src/Data/String/CodePoints.purs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 8492087..9083c10 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -81,7 +81,7 @@ codePointFromChar = fromEnum >>> CodePoint -- | constant space and time. -- | -- | ```purescript --- | >>> map singleton (codePointFromInt 0x1D400) +-- | >>> map singleton (toEnum 0x1D400) -- | Just "𝐀" -- | ``` -- | From a455b8cd32aa305e1d5f3cd3500713f51a9b73f5 Mon Sep 17 00:00:00 2001 From: Rintcius Blok Date: Fri, 20 Sep 2019 15:57:13 +0200 Subject: [PATCH 155/186] Fix tests for purs 0.13 (#119) --- test/Test/Data/String/CodePoints.purs | 40 +++++++++++++-------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/test/Test/Data/String/CodePoints.purs b/test/Test/Data/String/CodePoints.purs index a3e49b9..310321b 100644 --- a/test/Test/Data/String/CodePoints.purs +++ b/test/Test/Data/String/CodePoints.purs @@ -12,7 +12,7 @@ import Partial.Unsafe (unsafePartial) import Test.Assert (assertEqual) str :: String -str = "a\xDC00\xD800\xD800\x16805\x16A06\&z" +str = "a\xDC00\xD800\xD800\x16805\x16A06z" testStringCodePoints :: Effect Unit testStringCodePoints = do @@ -112,23 +112,23 @@ testStringCodePoints = do log "uncons" assertEqual { actual: SCP.uncons str - , expected: Just {head: cp 0x61, tail: "\xDC00\xD800\xD800\x16805\x16A06\&z"} + , expected: Just {head: cp 0x61, tail: "\xDC00\xD800\xD800\x16805\x16A06z"} } assertEqual { actual: SCP.uncons (SCP.drop 1 str) - , expected: Just {head: cp 0xDC00, tail: "\xD800\xD800\x16805\x16A06\&z"} + , expected: Just {head: cp 0xDC00, tail: "\xD800\xD800\x16805\x16A06z"} } assertEqual { actual: SCP.uncons (SCP.drop 2 str) - , expected: Just {head: cp 0xD800, tail: "\xD800\x16805\x16A06\&z"} + , expected: Just {head: cp 0xD800, tail: "\xD800\x16805\x16A06z"} } assertEqual { actual: SCP.uncons (SCP.drop 3 str) - , expected: Just {head: cp 0xD800, tail: "\x16805\x16A06\&z"} + , expected: Just {head: cp 0xD800, tail: "\x16805\x16A06z"} } assertEqual { actual: SCP.uncons (SCP.drop 4 str) - , expected: Just {head: cp 0x16805, tail: "\x16A06\&z"} + , expected: Just {head: cp 0x16805, tail: "\x16A06z"} } assertEqual { actual: SCP.uncons (SCP.drop 5 str) @@ -233,7 +233,7 @@ testStringCodePoints = do , expected: Just 6 } assertEqual - { actual: SCP.indexOf (Pattern "\0") str + { actual: SCP.indexOf (Pattern "\n") str , expected: Nothing } assertEqual @@ -345,7 +345,7 @@ testStringCodePoints = do , expected: Just 6 } assertEqual - { actual: SCP.lastIndexOf (Pattern "\0") str + { actual: SCP.lastIndexOf (Pattern "\n") str , expected: Nothing } assertEqual @@ -530,23 +530,23 @@ testStringCodePoints = do } assertEqual { actual: SCP.drop 1 str - , expected: "\xDC00\xD800\xD800\x16805\x16A06\&z" + , expected: "\xDC00\xD800\xD800\x16805\x16A06z" } assertEqual { actual: SCP.drop 2 str - , expected: "\xD800\xD800\x16805\x16A06\&z" + , expected: "\xD800\xD800\x16805\x16A06z" } assertEqual { actual: SCP.drop 3 str - , expected: "\xD800\x16805\x16A06\&z" + , expected: "\xD800\x16805\x16A06z" } assertEqual { actual: SCP.drop 4 str - , expected: "\x16805\x16A06\&z" + , expected: "\x16805\x16A06z" } assertEqual { actual: SCP.drop 5 str - , expected: "\x16A06\&z" + , expected: "\x16A06z" } assertEqual { actual: SCP.drop 6 str @@ -572,11 +572,11 @@ testStringCodePoints = do } assertEqual { actual: SCP.dropWhile (\c -> fromEnum c < 0xFFFF) str - , expected: "\x16805\x16A06\&z" + , expected: "\x16805\x16A06z" } assertEqual { actual: SCP.dropWhile (\c -> fromEnum c < 0xDC00) str - , expected: "\xDC00\xD800\xD800\x16805\x16A06\&z" + , expected: "\xDC00\xD800\xD800\x16805\x16A06z" } log "splitAt" @@ -610,23 +610,23 @@ testStringCodePoints = do } assertEqual { actual: SCP.splitAt 1 str - , expected: {before: "a", after: "\xDC00\xD800\xD800\x16805\x16A06\&z"} + , expected: {before: "a", after: "\xDC00\xD800\xD800\x16805\x16A06z"} } assertEqual { actual: SCP.splitAt 2 str - , expected: {before: "a\xDC00", after: "\xD800\xD800\x16805\x16A06\&z"} + , expected: {before: "a\xDC00", after: "\xD800\xD800\x16805\x16A06z"} } assertEqual { actual: SCP.splitAt 3 str - , expected: {before: "a\xDC00\xD800", after: "\xD800\x16805\x16A06\&z"} + , expected: {before: "a\xDC00\xD800", after: "\xD800\x16805\x16A06z"} } assertEqual { actual: SCP.splitAt 4 str - , expected: {before: "a\xDC00\xD800\xD800", after: "\x16805\x16A06\&z"} + , expected: {before: "a\xDC00\xD800\xD800", after: "\x16805\x16A06z"} } assertEqual { actual: SCP.splitAt 5 str - , expected: {before: "a\xDC00\xD800\xD800\x16805", after: "\x16A06\&z"} + , expected: {before: "a\xDC00\xD800\xD800\x16805", after: "\x16A06z"} } assertEqual { actual: SCP.splitAt 6 str From 1c7f254639f5f5bfc668bd9b2bdce66d17c5d3e9 Mon Sep 17 00:00:00 2001 From: Cyril Date: Sat, 14 Mar 2020 23:02:00 +0100 Subject: [PATCH 156/186] Fix the retrieval of the latest compiler version tag (#122) See https://discourse.purescript.org/t/new-404-ci-failures-in-core-libraries/1225. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4cbd5fd..8daa38c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,8 +5,8 @@ node_js: stable env: - PATH=$HOME/purescript:$PATH install: - - TAG=$(wget -q -O - https://github.com/purescript/purescript/releases/latest --server-response --max-redirect 0 2>&1 | sed -n -e 's/.*Location:.*tag\///p') - - wget -O $HOME/purescript.tar.gz https://github.com/purescript/purescript/releases/download/$TAG/linux64.tar.gz + - TAG=$(basename $(curl --location --silent --output /dev/null -w %{url_effective} https://github.com/purescript/purescript/releases/latest)) + - curl --location --output $HOME/purescript.tar.gz https://github.com/purescript/purescript/releases/download/$TAG/linux64.tar.gz - tar -xvf $HOME/purescript.tar.gz -C $HOME/ - chmod a+x $HOME/purescript - npm install -g bower From eefc8b04c16bce4669ffe88f9d5eeb6333bb2382 Mon Sep 17 00:00:00 2001 From: Cyril Date: Sat, 14 Mar 2020 23:02:48 +0100 Subject: [PATCH 157/186] Remove primes from foreign modules exports (#121) --- src/Data/String/CodeUnits.js | 4 ++-- src/Data/String/CodeUnits.purs | 8 ++++---- src/Data/String/Regex.js | 8 ++++---- src/Data/String/Regex.purs | 17 ++++++++++------- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/Data/String/CodeUnits.js b/src/Data/String/CodeUnits.js index 6590986..870b96f 100644 --- a/src/Data/String/CodeUnits.js +++ b/src/Data/String/CodeUnits.js @@ -53,7 +53,7 @@ exports._indexOf = function (just) { }; }; -exports["_indexOf'"] = function (just) { +exports._indexOfStartingAt = function (just) { return function (nothing) { return function (x) { return function (startAt) { @@ -78,7 +78,7 @@ exports._lastIndexOf = function (just) { }; }; -exports["_lastIndexOf'"] = function (just) { +exports._lastIndexOfStartingAt = function (just) { return function (nothing) { return function (x) { return function (startAt) { diff --git a/src/Data/String/CodeUnits.purs b/src/Data/String/CodeUnits.purs index 753a0c0..362b720 100644 --- a/src/Data/String/CodeUnits.purs +++ b/src/Data/String/CodeUnits.purs @@ -188,9 +188,9 @@ foreign import _indexOf -- | ``` -- | indexOf' :: Pattern -> Int -> String -> Maybe Int -indexOf' = _indexOf' Just Nothing +indexOf' = _indexOfStartingAt Just Nothing -foreign import _indexOf' +foreign import _indexOfStartingAt :: (forall a. a -> Maybe a) -> (forall a. Maybe a) -> Pattern @@ -228,9 +228,9 @@ foreign import _lastIndexOf -- | ``` -- | lastIndexOf' :: Pattern -> Int -> String -> Maybe Int -lastIndexOf' = _lastIndexOf' Just Nothing +lastIndexOf' = _lastIndexOfStartingAt Just Nothing -foreign import _lastIndexOf' +foreign import _lastIndexOfStartingAt :: (forall a. a -> Maybe a) -> (forall a. Maybe a) -> Pattern diff --git a/src/Data/String/Regex.js b/src/Data/String/Regex.js index 0b84c5b..ae40c11 100644 --- a/src/Data/String/Regex.js +++ b/src/Data/String/Regex.js @@ -1,10 +1,10 @@ "use strict"; -exports["showRegex'"] = function (r) { +exports.showRegexImpl = function (r) { return "" + r; }; -exports["regex'"] = function (left) { +exports.regexImpl = function (left) { return function (right) { return function (s1) { return function (s2) { @@ -22,7 +22,7 @@ exports.source = function (r) { return r.source; }; -exports["flags'"] = function (r) { +exports.flagsImpl = function (r) { return { multiline: r.multiline, ignoreCase: r.ignoreCase, @@ -67,7 +67,7 @@ exports.replace = function (r) { }; }; -exports["replace'"] = function (r) { +exports.replaceBy = function (r) { return function (f) { return function (s2) { return s2.replace(r, function (match) { diff --git a/src/Data/String/Regex.purs b/src/Data/String/Regex.purs index d42145a..4f2e74f 100644 --- a/src/Data/String/Regex.purs +++ b/src/Data/String/Regex.purs @@ -28,12 +28,12 @@ import Data.String.Regex.Flags (RegexFlags(..), RegexFlagsRec) -- | Wraps Javascript `RegExp` objects. foreign import data Regex :: Type -foreign import showRegex' :: Regex -> String +foreign import showRegexImpl :: Regex -> String instance showRegex :: Show Regex where - show = showRegex' + show = showRegexImpl -foreign import regex' +foreign import regexImpl :: (String -> Either String Regex) -> (Regex -> Either String Regex) -> String @@ -43,17 +43,17 @@ foreign import regex' -- | Constructs a `Regex` from a pattern string and flags. Fails with -- | `Left error` if the pattern contains a syntax error. regex :: String -> RegexFlags -> Either String Regex -regex s f = regex' Left Right s $ renderFlags f +regex s f = regexImpl Left Right s $ renderFlags f -- | Returns the pattern string used to construct the given `Regex`. foreign import source :: Regex -> String -- | Returns the `RegexFlags` used to construct the given `Regex`. flags :: Regex -> RegexFlags -flags = RegexFlags <<< flags' +flags = RegexFlags <<< flagsImpl -- | Returns the `RegexFlags` inner record used to construct the given `Regex`. -foreign import flags' :: Regex -> RegexFlagsRec +foreign import flagsImpl :: Regex -> RegexFlagsRec -- | Returns the string representation of the given `RegexFlags`. renderFlags :: RegexFlags -> String @@ -101,7 +101,10 @@ foreign import replace :: Regex -> String -> String -> String -- | Transforms occurences of the `Regex` using a function of the matched -- | substring and a list of submatch strings. -- | 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 +replace' :: Regex -> (String -> Array String -> String) -> String -> String +replace' = replaceBy + +foreign import replaceBy :: Regex -> (String -> Array String -> String) -> String -> String foreign import _search :: (forall r. r -> Maybe r) From 8813b42e9b5775daf25124e598f103f090cffe5f Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Tue, 12 May 2020 14:40:25 -0700 Subject: [PATCH 158/186] improve performance of stripPrefix/stripSuffix; add tests --- src/Data/String/CodeUnits.purs | 14 ++++---- test/Test/Data/String.purs | 24 +++++-------- test/Test/Data/String/CodeUnits.purs | 52 ++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 24 deletions(-) diff --git a/src/Data/String/CodeUnits.purs b/src/Data/String/CodeUnits.purs index 362b720..cc919e0 100644 --- a/src/Data/String/CodeUnits.purs +++ b/src/Data/String/CodeUnits.purs @@ -44,10 +44,9 @@ import Data.String.Unsafe as U -- | stripPrefix (Pattern "http:") "https://purescript.org" == Nothing -- | ``` stripPrefix :: Pattern -> String -> Maybe String -stripPrefix prefix@(Pattern prefixS) str = - case indexOf prefix str of - Just 0 -> Just $ drop (length prefixS) str - _ -> Nothing +stripPrefix (Pattern prefix) str = + let { before, after } = splitAt (length prefix) str in + if before == prefix then Just after else Nothing -- | If the string ends with the given suffix, return the portion of the -- | string left after removing it, as a `Just` value. Otherwise, return @@ -58,10 +57,9 @@ stripPrefix prefix@(Pattern prefixS) str = -- | stripSuffix (Pattern ".exe") "psc" == Nothing -- | ``` stripSuffix :: Pattern -> String -> Maybe String -stripSuffix suffix@(Pattern suffixS) str = - case lastIndexOf suffix str of - Just x | x == length str - length suffixS -> Just $ take x str - _ -> Nothing +stripSuffix (Pattern suffix) str = + let { before, after } = splitAt (length str - length suffix) str in + if after == suffix then Just before else Nothing -- | Checks whether the pattern appears in the given string. -- | diff --git a/test/Test/Data/String.purs b/test/Test/Data/String.purs index c496798..5c73153 100644 --- a/test/Test/Data/String.purs +++ b/test/Test/Data/String.purs @@ -17,25 +17,17 @@ testString = do assert $ not (S.null "a") log "stripPrefix" + -- this is a re-export from Data.String.CodeUnits, so the majority of tests are in there assertEqual - { actual: S.stripPrefix (Pattern "") "" - , expected: Just "" - } - assertEqual - { actual: S.stripPrefix (Pattern "") "abc" - , expected: Just "abc" - } - assertEqual - { actual: S.stripPrefix (Pattern "a") "abc" - , expected: Just "bc" - } - assertEqual - { actual: S.stripPrefix (Pattern "!") "abc" - , expected: Nothing + { actual: S.stripPrefix (Pattern "𝕒𝕓𝕔") "𝕒𝕓𝕔𝕕𝕖" + , expected: Just "𝕕𝕖" } + + log "stripSuffix" + -- this is a re-export from Data.String.CodeUnits, so the majority of tests are in there assertEqual - { actual: S.stripPrefix (Pattern "!") "" - , expected: Nothing + { actual: S.stripSuffix (Pattern "𝕔𝕕𝕖") "𝕒𝕓𝕔𝕕𝕖" + , expected: Just "𝕒𝕓" } log "contains" diff --git a/test/Test/Data/String/CodeUnits.purs b/test/Test/Data/String/CodeUnits.purs index 8106ba8..249a639 100644 --- a/test/Test/Data/String/CodeUnits.purs +++ b/test/Test/Data/String/CodeUnits.purs @@ -12,6 +12,58 @@ import Test.Assert (assert, assertEqual) testStringCodeUnits :: Effect Unit testStringCodeUnits = do + log "stripPrefix" + assertEqual + { actual: SCU.stripPrefix (Pattern "abc") "abcde" + , expected: Just "de" + } + assertEqual + { actual: SCU.stripPrefix (Pattern "xyz") "abcde" + , expected: Nothing + } + assertEqual + { actual: SCU.stripPrefix (Pattern "abcd") "ab" + , expected: Nothing + } + assertEqual + { actual: SCU.stripPrefix (Pattern "abc") "abc" + , expected: Just "" + } + assertEqual + { actual: SCU.stripPrefix (Pattern "") "abc" + , expected: Just "abc" + } + assertEqual + { actual: SCU.stripPrefix (Pattern "") "" + , expected: Just "" + } + + log "stripSuffix" + assertEqual + { actual: SCU.stripSuffix (Pattern "cde") "abcde" + , expected: Just "ab" + } + assertEqual + { actual: SCU.stripSuffix (Pattern "xyz") "abcde" + , expected: Nothing + } + assertEqual + { actual: SCU.stripSuffix (Pattern "abcd") "cd" + , expected: Nothing + } + assertEqual + { actual: SCU.stripSuffix (Pattern "abc") "abc" + , expected: Just "" + } + assertEqual + { actual: SCU.stripSuffix (Pattern "") "abc" + , expected: Just "abc" + } + assertEqual + { actual: SCU.stripSuffix (Pattern "") "" + , expected: Just "" + } + log "charAt" assertEqual { actual: SCU.charAt 0 "" From 79981caa15e817f84f8b5a22198ac133b408ba40 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Sun, 7 Jun 2020 15:19:49 +0100 Subject: [PATCH 159/186] Bump pulp version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 28a3fd3..e868f88 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ }, "devDependencies": { "eslint": "^4.19.1", - "pulp": "^12.2.0", + "pulp": "^15.0.0", "purescript-psa": "^0.6.0", "rimraf": "^2.6.2" } From cc98047aea84eb2ea18969998cc611e176d8d671 Mon Sep 17 00:00:00 2001 From: JordanMartinez Date: Sun, 15 Nov 2020 07:02:28 -0800 Subject: [PATCH 160/186] Update to v0.14.0-rc3 (#129) * Update TAG to v0.14.0-rc3; dependencies to master; psa to v0.8.0 * Account for `fromRight` breaking change --- .travis.yml | 3 ++- bower.json | 38 +++++++++++++++---------------- package.json | 2 +- src/Data/String/Regex/Unsafe.purs | 7 +++--- 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8daa38c..116705f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,8 @@ node_js: stable env: - PATH=$HOME/purescript:$PATH install: - - TAG=$(basename $(curl --location --silent --output /dev/null -w %{url_effective} https://github.com/purescript/purescript/releases/latest)) + # - TAG=$(basename $(curl --location --silent --output /dev/null -w %{url_effective} https://github.com/purescript/purescript/releases/latest)) + - TAG=v0.14.0-rc3 - curl --location --output $HOME/purescript.tar.gz https://github.com/purescript/purescript/releases/download/$TAG/linux64.tar.gz - tar -xvf $HOME/purescript.tar.gz -C $HOME/ - chmod a+x $HOME/purescript diff --git a/bower.json b/bower.json index ec7fb7f..e401464 100644 --- a/bower.json +++ b/bower.json @@ -16,26 +16,26 @@ "package.json" ], "dependencies": { - "purescript-arrays": "^5.0.0", - "purescript-control": "^4.0.0", - "purescript-either": "^4.0.0", - "purescript-enums": "^4.0.0", - "purescript-foldable-traversable": "^4.0.0", - "purescript-gen": "^2.0.0", - "purescript-integers": "^4.0.0", - "purescript-maybe": "^4.0.0", - "purescript-newtype": "^3.0.0", - "purescript-nonempty": "^5.0.0", - "purescript-partial": "^2.0.0", - "purescript-prelude": "^4.0.0", - "purescript-tailrec": "^4.0.0", - "purescript-tuples": "^5.0.0", - "purescript-unfoldable": "^4.0.0", - "purescript-unsafe-coerce": "^4.0.0" + "purescript-arrays": "master", + "purescript-control": "master", + "purescript-either": "master", + "purescript-enums": "master", + "purescript-foldable-traversable": "master", + "purescript-gen": "master", + "purescript-integers": "master", + "purescript-maybe": "master", + "purescript-newtype": "master", + "purescript-nonempty": "master", + "purescript-partial": "master", + "purescript-prelude": "master", + "purescript-tailrec": "master", + "purescript-tuples": "master", + "purescript-unfoldable": "master", + "purescript-unsafe-coerce": "master" }, "devDependencies": { - "purescript-assert": "^4.0.0", - "purescript-console": "^4.0.0", - "purescript-minibench": "^2.0.0" + "purescript-assert": "master", + "purescript-console": "master", + "purescript-minibench": "master" } } diff --git a/package.json b/package.json index e868f88..1f09388 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "devDependencies": { "eslint": "^4.19.1", "pulp": "^15.0.0", - "purescript-psa": "^0.6.0", + "purescript-psa": "^0.8.0", "rimraf": "^2.6.2" } } diff --git a/src/Data/String/Regex/Unsafe.purs b/src/Data/String/Regex/Unsafe.purs index 0a982c6..3cbd0ae 100644 --- a/src/Data/String/Regex/Unsafe.purs +++ b/src/Data/String/Regex/Unsafe.purs @@ -2,13 +2,14 @@ module Data.String.Regex.Unsafe ( unsafeRegex ) where -import Data.Either (fromRight) +import Control.Category (identity) +import Data.Either (either) import Data.String.Regex (Regex, regex) import Data.String.Regex.Flags (RegexFlags) -import Partial.Unsafe (unsafePartial) +import Partial.Unsafe (unsafeCrashWith) -- | Constructs a `Regex` from a pattern string and flags. Fails with -- | an exception if the pattern contains a syntax error. unsafeRegex :: String -> RegexFlags -> Regex -unsafeRegex s f = unsafePartial fromRight (regex s f) +unsafeRegex s f = either unsafeCrashWith identity (regex s f) From 176633dc6d540b7ec279d970ead65b2ce2c734aa Mon Sep 17 00:00:00 2001 From: toastal Date: Thu, 19 Nov 2020 23:47:22 +0000 Subject: [PATCH 161/186] Fix line endings and formatting to match project (#132) --- src/Data/String/Regex/Unsafe.purs | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/Data/String/Regex/Unsafe.purs b/src/Data/String/Regex/Unsafe.purs index 3cbd0ae..8afd1a2 100644 --- a/src/Data/String/Regex/Unsafe.purs +++ b/src/Data/String/Regex/Unsafe.purs @@ -1,15 +1,14 @@ -module Data.String.Regex.Unsafe -( unsafeRegex -) where - -import Control.Category (identity) -import Data.Either (either) -import Data.String.Regex (Regex, regex) -import Data.String.Regex.Flags (RegexFlags) - -import Partial.Unsafe (unsafeCrashWith) - --- | Constructs a `Regex` from a pattern string and flags. Fails with --- | an exception if the pattern contains a syntax error. -unsafeRegex :: String -> RegexFlags -> Regex -unsafeRegex s f = either unsafeCrashWith identity (regex s f) +module Data.String.Regex.Unsafe + ( unsafeRegex + ) where + +import Control.Category (identity) +import Data.Either (either) +import Data.String.Regex (Regex, regex) +import Data.String.Regex.Flags (RegexFlags) +import Partial.Unsafe (unsafeCrashWith) + +-- | Constructs a `Regex` from a pattern string and flags. Fails with +-- | an exception if the pattern contains a syntax error. +unsafeRegex :: String -> RegexFlags -> Regex +unsafeRegex s f = either unsafeCrashWith identity (regex s f) From 848b3309e4325a30cf60e295e4a266806df9a777 Mon Sep 17 00:00:00 2001 From: Cyril Sobierajewicz Date: Mon, 23 Nov 2020 21:06:28 +0100 Subject: [PATCH 162/186] Replace monomorphic proxies by Type.Proxy.Proxy and polymorphic variables --- src/Data/String/NonEmpty/Internal.purs | 6 +- test/Test/Data/String/NonEmpty.purs | 138 +++++------ test/Test/Data/String/NonEmpty/CodeUnits.purs | 234 +++++++++--------- 3 files changed, 189 insertions(+), 189 deletions(-) diff --git a/src/Data/String/NonEmpty/Internal.purs b/src/Data/String/NonEmpty/Internal.purs index bfd2984..7a6197e 100644 --- a/src/Data/String/NonEmpty/Internal.purs +++ b/src/Data/String/NonEmpty/Internal.purs @@ -8,7 +8,7 @@ import Data.Maybe (Maybe(..), fromJust) import Data.Semigroup.Foldable (class Foldable1) import Data.String as String import Data.String.Pattern (Pattern) -import Data.Symbol (class IsSymbol, SProxy, reflectSymbol) +import Data.Symbol (class IsSymbol, reflectSymbol) import Prim.TypeError as TE import Unsafe.Coerce (unsafeCoerce) @@ -26,10 +26,10 @@ instance showNonEmptyString :: Show NonEmptyString where -- | -- | ``` purescript -- | something :: NonEmptyString --- | something = nes (SProxy :: SProxy "something") +-- | something = nes (Proxy :: Proxy "something") -- | ``` class MakeNonEmpty (s :: Symbol) where - nes :: SProxy s -> NonEmptyString + nes :: forall proxy. proxy s -> NonEmptyString instance makeNonEmptyBad :: TE.Fail (TE.Text "Cannot create an NonEmptyString from an empty Symbol") => MakeNonEmpty "" where nes _ = NonEmptyString "" diff --git a/test/Test/Data/String/NonEmpty.purs b/test/Test/Data/String/NonEmpty.purs index 59a8f22..a4103ec 100644 --- a/test/Test/Data/String/NonEmpty.purs +++ b/test/Test/Data/String/NonEmpty.purs @@ -6,11 +6,11 @@ import Data.Array.NonEmpty as NEA import Data.Maybe (Maybe(..), fromJust) import Data.String.NonEmpty (Pattern(..), nes) import Data.String.NonEmpty as NES -import Data.Symbol (SProxy(..)) import Effect (Effect) import Effect.Console (log) import Partial.Unsafe (unsafePartial) import Test.Assert (assert, assertEqual) +import Type.Proxy (Proxy(..)) testNonEmptyString :: Effect Unit testNonEmptyString = do @@ -22,7 +22,7 @@ testNonEmptyString = do } assertEqual { actual: NES.fromString "hello" - , expected: Just (nes (SProxy :: SProxy "hello")) + , expected: Just (nes (Proxy :: Proxy "hello")) } log "toString" @@ -33,136 +33,136 @@ testNonEmptyString = do log "appendString" assertEqual - { actual: NES.appendString (nes (SProxy :: SProxy "Hello")) " world" - , expected: nes (SProxy :: SProxy "Hello world") + { actual: NES.appendString (nes (Proxy :: Proxy "Hello")) " world" + , expected: nes (Proxy :: Proxy "Hello world") } assertEqual - { actual: NES.appendString (nes (SProxy :: SProxy "Hello")) "" - , expected: nes (SProxy :: SProxy "Hello") + { actual: NES.appendString (nes (Proxy :: Proxy "Hello")) "" + , expected: nes (Proxy :: Proxy "Hello") } log "prependString" assertEqual - { actual: NES.prependString "be" (nes (SProxy :: SProxy "fore")) - , expected: nes (SProxy :: SProxy "before") + { actual: NES.prependString "be" (nes (Proxy :: Proxy "fore")) + , expected: nes (Proxy :: Proxy "before") } assertEqual - { actual: NES.prependString "" (nes (SProxy :: SProxy "fore")) - , expected: nes (SProxy :: SProxy "fore") + { actual: NES.prependString "" (nes (Proxy :: Proxy "fore")) + , expected: nes (Proxy :: Proxy "fore") } log "contains" - assert $ NES.contains (Pattern "") (nes (SProxy :: SProxy "abcd")) - assert $ NES.contains (Pattern "bc") (nes (SProxy :: SProxy "abcd")) - assert $ not NES.contains (Pattern "cb") (nes (SProxy :: SProxy "abcd")) - assert $ NES.contains (Pattern "needle") (nes (SProxy :: SProxy "haystack with needle")) - assert $ not NES.contains (Pattern "needle") (nes (SProxy :: SProxy "haystack")) + assert $ NES.contains (Pattern "") (nes (Proxy :: Proxy "abcd")) + assert $ NES.contains (Pattern "bc") (nes (Proxy :: Proxy "abcd")) + assert $ not NES.contains (Pattern "cb") (nes (Proxy :: Proxy "abcd")) + assert $ NES.contains (Pattern "needle") (nes (Proxy :: Proxy "haystack with needle")) + assert $ not NES.contains (Pattern "needle") (nes (Proxy :: Proxy "haystack")) log "localeCompare" assertEqual - { actual: NES.localeCompare (nes (SProxy :: SProxy "a")) (nes (SProxy :: SProxy "a")) + { actual: NES.localeCompare (nes (Proxy :: Proxy "a")) (nes (Proxy :: Proxy "a")) , expected: EQ } assertEqual - { actual: NES.localeCompare (nes (SProxy :: SProxy "a")) (nes (SProxy :: SProxy "b")) + { actual: NES.localeCompare (nes (Proxy :: Proxy "a")) (nes (Proxy :: Proxy "b")) , expected: LT } assertEqual - { actual: NES.localeCompare (nes (SProxy :: SProxy "b")) (nes (SProxy :: SProxy "a")) + { actual: NES.localeCompare (nes (Proxy :: Proxy "b")) (nes (Proxy :: Proxy "a")) , expected: GT } log "replace" assertEqual - { actual: NES.replace (Pattern "b") (NES.NonEmptyReplacement (nes (SProxy :: SProxy "!"))) (nes (SProxy :: SProxy "abc")) - , expected: nes (SProxy :: SProxy "a!c") + { actual: NES.replace (Pattern "b") (NES.NonEmptyReplacement (nes (Proxy :: Proxy "!"))) (nes (Proxy :: Proxy "abc")) + , expected: nes (Proxy :: Proxy "a!c") } assertEqual - { actual: NES.replace (Pattern "b") (NES.NonEmptyReplacement (nes (SProxy :: SProxy "!"))) (nes (SProxy :: SProxy "abbc")) - , expected: nes (SProxy :: SProxy "a!bc") + { actual: NES.replace (Pattern "b") (NES.NonEmptyReplacement (nes (Proxy :: Proxy "!"))) (nes (Proxy :: Proxy "abbc")) + , expected: nes (Proxy :: Proxy "a!bc") } assertEqual - { actual: NES.replace (Pattern "d") (NES.NonEmptyReplacement (nes (SProxy :: SProxy "!"))) (nes (SProxy :: SProxy "abc")) - , expected: nes (SProxy :: SProxy "abc") + { actual: NES.replace (Pattern "d") (NES.NonEmptyReplacement (nes (Proxy :: Proxy "!"))) (nes (Proxy :: Proxy "abc")) + , expected: nes (Proxy :: Proxy "abc") } log "replaceAll" assertEqual - { actual: NES.replaceAll (Pattern "[b]") (NES.NonEmptyReplacement (nes (SProxy :: SProxy "!"))) (nes (SProxy :: SProxy "a[b]c")) - , expected: nes (SProxy :: SProxy "a!c") + { actual: NES.replaceAll (Pattern "[b]") (NES.NonEmptyReplacement (nes (Proxy :: Proxy "!"))) (nes (Proxy :: Proxy "a[b]c")) + , expected: nes (Proxy :: Proxy "a!c") } assertEqual - { actual: NES.replaceAll (Pattern "[b]") (NES.NonEmptyReplacement (nes (SProxy :: SProxy "!"))) (nes (SProxy :: SProxy "a[b]c[b]")) - , expected: nes (SProxy :: SProxy "a!c!") + { actual: NES.replaceAll (Pattern "[b]") (NES.NonEmptyReplacement (nes (Proxy :: Proxy "!"))) (nes (Proxy :: Proxy "a[b]c[b]")) + , expected: nes (Proxy :: Proxy "a!c!") } assertEqual - { actual: NES.replaceAll (Pattern "x") (NES.NonEmptyReplacement (nes (SProxy :: SProxy "!"))) (nes (SProxy :: SProxy "abc")) - , expected: nes (SProxy :: SProxy "abc") + { actual: NES.replaceAll (Pattern "x") (NES.NonEmptyReplacement (nes (Proxy :: Proxy "!"))) (nes (Proxy :: Proxy "abc")) + , expected: nes (Proxy :: Proxy "abc") } log "stripPrefix" assertEqual - { actual: NES.stripPrefix (Pattern "") (nes (SProxy :: SProxy "abc")) - , expected: Just (nes (SProxy :: SProxy "abc")) + { actual: NES.stripPrefix (Pattern "") (nes (Proxy :: Proxy "abc")) + , expected: Just (nes (Proxy :: Proxy "abc")) } assertEqual - { actual: NES.stripPrefix (Pattern "a") (nes (SProxy :: SProxy "abc")) - , expected: Just (nes (SProxy :: SProxy "bc")) + { actual: NES.stripPrefix (Pattern "a") (nes (Proxy :: Proxy "abc")) + , expected: Just (nes (Proxy :: Proxy "bc")) } assertEqual - { actual: NES.stripPrefix (Pattern "abc") (nes (SProxy :: SProxy "abc")) + { actual: NES.stripPrefix (Pattern "abc") (nes (Proxy :: Proxy "abc")) , expected: Nothing } assertEqual - { actual: NES.stripPrefix (Pattern "!") (nes (SProxy :: SProxy "abc")) + { actual: NES.stripPrefix (Pattern "!") (nes (Proxy :: Proxy "abc")) , expected: Nothing } assertEqual - { actual: NES.stripPrefix (Pattern "http:") (nes (SProxy :: SProxy "http://purescript.org")) - , expected: Just (nes (SProxy :: SProxy "//purescript.org")) + { actual: NES.stripPrefix (Pattern "http:") (nes (Proxy :: Proxy "http://purescript.org")) + , expected: Just (nes (Proxy :: Proxy "//purescript.org")) } assertEqual - { actual: NES.stripPrefix (Pattern "http:") (nes (SProxy :: SProxy "https://purescript.org")) + { actual: NES.stripPrefix (Pattern "http:") (nes (Proxy :: Proxy "https://purescript.org")) , expected: Nothing } assertEqual - { actual: NES.stripPrefix (Pattern "Hello!") (nes (SProxy :: SProxy "Hello!")) + { actual: NES.stripPrefix (Pattern "Hello!") (nes (Proxy :: Proxy "Hello!")) , expected: Nothing } log "stripSuffix" assertEqual - { actual: NES.stripSuffix (Pattern ".exe") (nes (SProxy :: SProxy "purs.exe")) - , expected: Just (nes (SProxy :: SProxy "purs")) + { actual: NES.stripSuffix (Pattern ".exe") (nes (Proxy :: Proxy "purs.exe")) + , expected: Just (nes (Proxy :: Proxy "purs")) } assertEqual - { actual: NES.stripSuffix (Pattern ".exe") (nes (SProxy :: SProxy "purs")) + { actual: NES.stripSuffix (Pattern ".exe") (nes (Proxy :: Proxy "purs")) , expected: Nothing } assertEqual - { actual: NES.stripSuffix (Pattern "Hello!") (nes (SProxy :: SProxy "Hello!")) + { actual: NES.stripSuffix (Pattern "Hello!") (nes (Proxy :: Proxy "Hello!")) , expected: Nothing } log "toLower" assertEqual - { actual: NES.toLower (nes (SProxy :: SProxy "bAtMaN")) - , expected: nes (SProxy :: SProxy "batman") + { actual: NES.toLower (nes (Proxy :: Proxy "bAtMaN")) + , expected: nes (Proxy :: Proxy "batman") } log "toUpper" assertEqual - { actual: NES.toUpper (nes (SProxy :: SProxy "bAtMaN")) - , expected: nes (SProxy :: SProxy "BATMAN") + { actual: NES.toUpper (nes (Proxy :: Proxy "bAtMaN")) + , expected: nes (Proxy :: Proxy "BATMAN") } log "trim" assertEqual - { actual: NES.trim (nes (SProxy :: SProxy " abc ")) - , expected: Just (nes (SProxy :: SProxy "abc")) + { actual: NES.trim (nes (Proxy :: Proxy " abc ")) + , expected: Just (nes (Proxy :: Proxy "abc")) } assertEqual - { actual: NES.trim (nes (SProxy :: SProxy " \n")) + { actual: NES.trim (nes (Proxy :: Proxy " \n")) , expected: Nothing } @@ -172,48 +172,48 @@ testNonEmptyString = do , expected: "" } assertEqual - { actual: NES.joinWith "" [nes (SProxy :: SProxy "a"), nes (SProxy :: SProxy "b")] + { actual: NES.joinWith "" [nes (Proxy :: Proxy "a"), nes (Proxy :: Proxy "b")] , expected: "ab" } assertEqual - { actual: NES.joinWith "--" [nes (SProxy :: SProxy "a"), nes (SProxy :: SProxy "b"), nes (SProxy :: SProxy "c")] + { actual: NES.joinWith "--" [nes (Proxy :: Proxy "a"), nes (Proxy :: Proxy "b"), nes (Proxy :: Proxy "c")] , expected: "a--b--c" } log "join1With" assertEqual - { actual: NES.join1With "" (nea [nes (SProxy :: SProxy "a"), nes (SProxy :: SProxy "b")]) - , expected: nes (SProxy :: SProxy "ab") + { actual: NES.join1With "" (nea [nes (Proxy :: Proxy "a"), nes (Proxy :: Proxy "b")]) + , expected: nes (Proxy :: Proxy "ab") } assertEqual - { actual: NES.join1With "--" (nea [nes (SProxy :: SProxy "a"), nes (SProxy :: SProxy "b"), nes (SProxy :: SProxy "c")]) - , expected: nes (SProxy :: SProxy "a--b--c") + { actual: NES.join1With "--" (nea [nes (Proxy :: Proxy "a"), nes (Proxy :: Proxy "b"), nes (Proxy :: Proxy "c")]) + , expected: nes (Proxy :: Proxy "a--b--c") } assertEqual - { actual: NES.join1With ", " (nea [nes (SProxy :: SProxy "apple"), nes (SProxy :: SProxy "banana")]) - , expected: nes (SProxy :: SProxy "apple, banana") + { actual: NES.join1With ", " (nea [nes (Proxy :: Proxy "apple"), nes (Proxy :: Proxy "banana")]) + , expected: nes (Proxy :: Proxy "apple, banana") } assertEqual - { actual: NES.join1With "" (nea [nes (SProxy :: SProxy "apple"), nes (SProxy :: SProxy "banana")]) - , expected: nes (SProxy :: SProxy "applebanana") + { actual: NES.join1With "" (nea [nes (Proxy :: Proxy "apple"), nes (Proxy :: Proxy "banana")]) + , expected: nes (Proxy :: Proxy "applebanana") } log "joinWith1" assertEqual - { actual: NES.joinWith1 (nes (SProxy :: SProxy " ")) (nea ["a", "b"]) - , expected: nes (SProxy :: SProxy "a b") + { actual: NES.joinWith1 (nes (Proxy :: Proxy " ")) (nea ["a", "b"]) + , expected: nes (Proxy :: Proxy "a b") } assertEqual - { actual: NES.joinWith1 (nes (SProxy :: SProxy "--")) (nea ["a", "b", "c"]) - , expected: nes (SProxy :: SProxy "a--b--c") + { actual: NES.joinWith1 (nes (Proxy :: Proxy "--")) (nea ["a", "b", "c"]) + , expected: nes (Proxy :: Proxy "a--b--c") } assertEqual - { actual: NES.joinWith1 (nes (SProxy :: SProxy ", ")) (nea ["apple", "banana"]) - , expected: nes (SProxy :: SProxy "apple, banana") + { actual: NES.joinWith1 (nes (Proxy :: Proxy ", ")) (nea ["apple", "banana"]) + , expected: nes (Proxy :: Proxy "apple, banana") } assertEqual - { actual: NES.joinWith1 (nes (SProxy :: SProxy "/")) (nea ["a", "b", "", "c", ""]) - , expected: nes (SProxy :: SProxy "a/b//c/") + { actual: NES.joinWith1 (nes (Proxy :: Proxy "/")) (nea ["a", "b", "", "c", ""]) + , expected: nes (Proxy :: Proxy "a/b//c/") } nea :: Array ~> NEA.NonEmptyArray diff --git a/test/Test/Data/String/NonEmpty/CodeUnits.purs b/test/Test/Data/String/NonEmpty/CodeUnits.purs index fee9b51..0e78ee8 100644 --- a/test/Test/Data/String/NonEmpty/CodeUnits.purs +++ b/test/Test/Data/String/NonEmpty/CodeUnits.purs @@ -7,11 +7,11 @@ import Data.Enum (fromEnum) import Data.Maybe (Maybe(..), fromJust) import Data.String.NonEmpty (Pattern(..), nes) import Data.String.NonEmpty.CodeUnits as NESCU -import Data.Symbol (SProxy(..)) import Effect (Effect) import Effect.Console (log) import Partial.Unsafe (unsafePartial) import Test.Assert (assertEqual) +import Type.Proxy (Proxy(..)) testNonEmptyStringCodeUnits :: Effect Unit testNonEmptyStringCodeUnits = do @@ -23,7 +23,7 @@ testNonEmptyStringCodeUnits = do } assertEqual { actual: NESCU.fromCharArray ['a', 'b'] - , expected: Just (nes (SProxy :: SProxy "ab")) + , expected: Just (nes (Proxy :: Proxy "ab")) } log "fromNonEmptyCharArray" @@ -35,415 +35,415 @@ testNonEmptyStringCodeUnits = do log "singleton" assertEqual { actual: NESCU.singleton 'a' - , expected: nes (SProxy :: SProxy "a") + , expected: nes (Proxy :: Proxy "a") } log "cons" assertEqual { actual: NESCU.cons 'a' "bc" - , expected: nes (SProxy :: SProxy "abc") + , expected: nes (Proxy :: Proxy "abc") } assertEqual { actual: NESCU.cons 'a' "" - , expected: nes (SProxy :: SProxy "a") + , expected: nes (Proxy :: Proxy "a") } log "snoc" assertEqual { actual: NESCU.snoc 'c' "ab" - , expected: nes (SProxy :: SProxy "abc") + , expected: nes (Proxy :: Proxy "abc") } assertEqual { actual: NESCU.snoc 'a' "" - , expected: nes (SProxy :: SProxy "a") + , expected: nes (Proxy :: Proxy "a") } log "fromFoldable1" assertEqual { actual: NESCU.fromFoldable1 (nea ['a']) - , expected: nes (SProxy :: SProxy "a") + , expected: nes (Proxy :: Proxy "a") } assertEqual { actual: NESCU.fromFoldable1 (nea ['a', 'b', 'c']) - , expected: nes (SProxy :: SProxy "abc") + , expected: nes (Proxy :: Proxy "abc") } log "charAt" assertEqual - { actual: NESCU.charAt 0 (nes (SProxy :: SProxy "a")) + { actual: NESCU.charAt 0 (nes (Proxy :: Proxy "a")) , expected: Just 'a' } assertEqual - { actual: NESCU.charAt 1 (nes (SProxy :: SProxy "a")) + { actual: NESCU.charAt 1 (nes (Proxy :: Proxy "a")) , expected: Nothing } assertEqual - { actual: NESCU.charAt 0 (nes (SProxy :: SProxy "ab")) + { actual: NESCU.charAt 0 (nes (Proxy :: Proxy "ab")) , expected: Just 'a' } assertEqual - { actual: NESCU.charAt 1 (nes (SProxy :: SProxy "ab")) + { actual: NESCU.charAt 1 (nes (Proxy :: Proxy "ab")) , expected: Just 'b' } assertEqual - { actual: NESCU.charAt 2 (nes (SProxy :: SProxy "ab")) + { actual: NESCU.charAt 2 (nes (Proxy :: Proxy "ab")) , expected: Nothing } assertEqual - { actual: NESCU.charAt 2 (nes (SProxy :: SProxy "Hello")) + { actual: NESCU.charAt 2 (nes (Proxy :: Proxy "Hello")) , expected: Just 'l' } assertEqual - { actual: NESCU.charAt 10 (nes (SProxy :: SProxy "Hello")) + { actual: NESCU.charAt 10 (nes (Proxy :: Proxy "Hello")) , expected: Nothing } log "charCodeAt" assertEqual - { actual: fromEnum <$> NESCU.charAt 0 (nes (SProxy :: SProxy "a")) + { actual: fromEnum <$> NESCU.charAt 0 (nes (Proxy :: Proxy "a")) , expected: Just 97 } assertEqual - { actual: fromEnum <$> NESCU.charAt 1 (nes (SProxy :: SProxy "a")) + { actual: fromEnum <$> NESCU.charAt 1 (nes (Proxy :: Proxy "a")) , expected: Nothing } assertEqual - { actual: fromEnum <$> NESCU.charAt 0 (nes (SProxy :: SProxy "ab")) + { actual: fromEnum <$> NESCU.charAt 0 (nes (Proxy :: Proxy "ab")) , expected: Just 97 } assertEqual - { actual: fromEnum <$> NESCU.charAt 1 (nes (SProxy :: SProxy "ab")) + { actual: fromEnum <$> NESCU.charAt 1 (nes (Proxy :: Proxy "ab")) , expected: Just 98 } assertEqual - { actual: fromEnum <$> NESCU.charAt 2 (nes (SProxy :: SProxy "ab")) + { actual: fromEnum <$> NESCU.charAt 2 (nes (Proxy :: Proxy "ab")) , expected: Nothing } assertEqual - { actual: fromEnum <$> NESCU.charAt 2 (nes (SProxy :: SProxy "5 €")) + { actual: fromEnum <$> NESCU.charAt 2 (nes (Proxy :: Proxy "5 €")) , expected: Just 0x20AC } assertEqual - { actual: fromEnum <$> NESCU.charAt 10 (nes (SProxy :: SProxy "5 €")) + { actual: fromEnum <$> NESCU.charAt 10 (nes (Proxy :: Proxy "5 €")) , expected: Nothing } log "toChar" assertEqual - { actual: NESCU.toChar (nes (SProxy :: SProxy "a")) + { actual: NESCU.toChar (nes (Proxy :: Proxy "a")) , expected: Just 'a' } assertEqual - { actual: NESCU.toChar (nes (SProxy :: SProxy "ab")) + { actual: NESCU.toChar (nes (Proxy :: Proxy "ab")) , expected: Nothing } log "toCharArray" assertEqual - { actual: NESCU.toCharArray (nes (SProxy :: SProxy "a")) + { actual: NESCU.toCharArray (nes (Proxy :: Proxy "a")) , expected: ['a'] } assertEqual - { actual: NESCU.toCharArray (nes (SProxy :: SProxy "ab")) + { actual: NESCU.toCharArray (nes (Proxy :: Proxy "ab")) , expected: ['a', 'b'] } assertEqual - { actual: NESCU.toCharArray (nes (SProxy :: SProxy "Hello☺\n")) + { actual: NESCU.toCharArray (nes (Proxy :: Proxy "Hello☺\n")) , expected: ['H','e','l','l','o','☺','\n'] } log "toNonEmptyCharArray" assertEqual - { actual: NESCU.toNonEmptyCharArray (nes (SProxy :: SProxy "ab")) + { actual: NESCU.toNonEmptyCharArray (nes (Proxy :: Proxy "ab")) , expected: nea ['a', 'b'] } log "uncons" assertEqual - { actual: NESCU.uncons (nes (SProxy :: SProxy "a")) + { actual: NESCU.uncons (nes (Proxy :: Proxy "a")) , expected: { head: 'a', tail: Nothing } } assertEqual - { actual: NESCU.uncons (nes (SProxy :: SProxy "Hello World")) - , expected: { head: 'H', tail: Just (nes (SProxy :: SProxy "ello World")) } + { actual: NESCU.uncons (nes (Proxy :: Proxy "Hello World")) + , expected: { head: 'H', tail: Just (nes (Proxy :: Proxy "ello World")) } } log "takeWhile" assertEqual - { actual: NESCU.takeWhile (\c -> true) (nes (SProxy :: SProxy "abc")) - , expected: Just (nes (SProxy :: SProxy "abc")) + { actual: NESCU.takeWhile (\c -> true) (nes (Proxy :: Proxy "abc")) + , expected: Just (nes (Proxy :: Proxy "abc")) } assertEqual - { actual: NESCU.takeWhile (\c -> false) (nes (SProxy :: SProxy "abc")) + { actual: NESCU.takeWhile (\c -> false) (nes (Proxy :: Proxy "abc")) , expected: Nothing } assertEqual - { actual: NESCU.takeWhile (\c -> c /= 'b') (nes (SProxy :: SProxy "aabbcc")) - , expected: Just (nes (SProxy :: SProxy "aa")) + { actual: NESCU.takeWhile (\c -> c /= 'b') (nes (Proxy :: Proxy "aabbcc")) + , expected: Just (nes (Proxy :: Proxy "aa")) } assertEqual - { actual: NESCU.takeWhile (_ /= ':') (nes (SProxy :: SProxy "http://purescript.org")) - , expected: Just (nes (SProxy :: SProxy "http")) + { actual: NESCU.takeWhile (_ /= ':') (nes (Proxy :: Proxy "http://purescript.org")) + , expected: Just (nes (Proxy :: Proxy "http")) } assertEqual - { actual: NESCU.takeWhile (_ == 'a') (nes (SProxy :: SProxy "xyz")) + { actual: NESCU.takeWhile (_ == 'a') (nes (Proxy :: Proxy "xyz")) , expected: Nothing } log "dropWhile" assertEqual - { actual: NESCU.dropWhile (\c -> true) (nes (SProxy :: SProxy "abc")) + { actual: NESCU.dropWhile (\c -> true) (nes (Proxy :: Proxy "abc")) , expected: Nothing } assertEqual - { actual: NESCU.dropWhile (\c -> false) (nes (SProxy :: SProxy "abc")) - , expected: Just (nes (SProxy :: SProxy "abc")) + { actual: NESCU.dropWhile (\c -> false) (nes (Proxy :: Proxy "abc")) + , expected: Just (nes (Proxy :: Proxy "abc")) } assertEqual - { actual: NESCU.dropWhile (\c -> c /= 'b') (nes (SProxy :: SProxy "aabbcc")) - , expected: Just (nes (SProxy :: SProxy "bbcc")) + { actual: NESCU.dropWhile (\c -> c /= 'b') (nes (Proxy :: Proxy "aabbcc")) + , expected: Just (nes (Proxy :: Proxy "bbcc")) } assertEqual - { actual: NESCU.dropWhile (_ /= '.') (nes (SProxy :: SProxy "Test.purs")) - , expected: Just (nes (SProxy :: SProxy ".purs")) + { actual: NESCU.dropWhile (_ /= '.') (nes (Proxy :: Proxy "Test.purs")) + , expected: Just (nes (Proxy :: Proxy ".purs")) } log "indexOf" assertEqual - { actual: NESCU.indexOf (Pattern "") (nes (SProxy :: SProxy "abcd")) + { actual: NESCU.indexOf (Pattern "") (nes (Proxy :: Proxy "abcd")) , expected: Just 0 } assertEqual - { actual: NESCU.indexOf (Pattern "bc") (nes (SProxy :: SProxy "abcd")) + { actual: NESCU.indexOf (Pattern "bc") (nes (Proxy :: Proxy "abcd")) , expected: Just 1 } assertEqual - { actual: NESCU.indexOf (Pattern "cb") (nes (SProxy :: SProxy "abcd")) + { actual: NESCU.indexOf (Pattern "cb") (nes (Proxy :: Proxy "abcd")) , expected: Nothing } log "indexOf'" assertEqual - { actual: NESCU.indexOf' (Pattern "") (-1) (nes (SProxy :: SProxy "ab")) + { actual: NESCU.indexOf' (Pattern "") (-1) (nes (Proxy :: Proxy "ab")) , expected: Nothing } assertEqual - { actual: NESCU.indexOf' (Pattern "") 0 (nes (SProxy :: SProxy "ab")) + { actual: NESCU.indexOf' (Pattern "") 0 (nes (Proxy :: Proxy "ab")) , expected: Just 0 } assertEqual - { actual: NESCU.indexOf' (Pattern "") 1 (nes (SProxy :: SProxy "ab")) + { actual: NESCU.indexOf' (Pattern "") 1 (nes (Proxy :: Proxy "ab")) , expected: Just 1 } assertEqual - { actual: NESCU.indexOf' (Pattern "") 2 (nes (SProxy :: SProxy "ab")) + { actual: NESCU.indexOf' (Pattern "") 2 (nes (Proxy :: Proxy "ab")) , expected: Just 2 } assertEqual - { actual: NESCU.indexOf' (Pattern "") 3 (nes (SProxy :: SProxy "ab")) + { actual: NESCU.indexOf' (Pattern "") 3 (nes (Proxy :: Proxy "ab")) , expected: Nothing } assertEqual - { actual: NESCU.indexOf' (Pattern "bc") 0 (nes (SProxy :: SProxy "abcd")) + { actual: NESCU.indexOf' (Pattern "bc") 0 (nes (Proxy :: Proxy "abcd")) , expected: Just 1 } assertEqual - { actual: NESCU.indexOf' (Pattern "bc") 1 (nes (SProxy :: SProxy "abcd")) + { actual: NESCU.indexOf' (Pattern "bc") 1 (nes (Proxy :: Proxy "abcd")) , expected: Just 1 } assertEqual - { actual: NESCU.indexOf' (Pattern "bc") 2 (nes (SProxy :: SProxy "abcd")) + { actual: NESCU.indexOf' (Pattern "bc") 2 (nes (Proxy :: Proxy "abcd")) , expected: Nothing } assertEqual - { actual: NESCU.indexOf' (Pattern "cb") 0 (nes (SProxy :: SProxy "abcd")) + { actual: NESCU.indexOf' (Pattern "cb") 0 (nes (Proxy :: Proxy "abcd")) , expected: Nothing } log "lastIndexOf" assertEqual - { actual: NESCU.lastIndexOf (Pattern "") (nes (SProxy :: SProxy "abcd")) + { actual: NESCU.lastIndexOf (Pattern "") (nes (Proxy :: Proxy "abcd")) , expected: Just 4 } assertEqual - { actual: NESCU.lastIndexOf (Pattern "bc") (nes (SProxy :: SProxy "abcd")) + { actual: NESCU.lastIndexOf (Pattern "bc") (nes (Proxy :: Proxy "abcd")) , expected: Just 1 } assertEqual - { actual: NESCU.lastIndexOf (Pattern "cb") (nes (SProxy :: SProxy "abcd")) + { actual: NESCU.lastIndexOf (Pattern "cb") (nes (Proxy :: Proxy "abcd")) , expected: Nothing } log "lastIndexOf'" assertEqual - { actual: NESCU.lastIndexOf' (Pattern "") (-1) (nes (SProxy :: SProxy "ab")) + { actual: NESCU.lastIndexOf' (Pattern "") (-1) (nes (Proxy :: Proxy "ab")) , expected: Nothing } assertEqual - { actual: NESCU.lastIndexOf' (Pattern "") 0 (nes (SProxy :: SProxy "ab")) + { actual: NESCU.lastIndexOf' (Pattern "") 0 (nes (Proxy :: Proxy "ab")) , expected: Just 0 } assertEqual - { actual: NESCU.lastIndexOf' (Pattern "") 1 (nes (SProxy :: SProxy "ab")) + { actual: NESCU.lastIndexOf' (Pattern "") 1 (nes (Proxy :: Proxy "ab")) , expected: Just 1 } assertEqual - { actual: NESCU.lastIndexOf' (Pattern "") 2 (nes (SProxy :: SProxy "ab")) + { actual: NESCU.lastIndexOf' (Pattern "") 2 (nes (Proxy :: Proxy "ab")) , expected: Just 2 } assertEqual - { actual: NESCU.lastIndexOf' (Pattern "") 3 (nes (SProxy :: SProxy "ab")) + { actual: NESCU.lastIndexOf' (Pattern "") 3 (nes (Proxy :: Proxy "ab")) , expected: Nothing } assertEqual - { actual: NESCU.lastIndexOf' (Pattern "bc") 0 (nes (SProxy :: SProxy "abcd")) + { actual: NESCU.lastIndexOf' (Pattern "bc") 0 (nes (Proxy :: Proxy "abcd")) , expected: Nothing } assertEqual - { actual: NESCU.lastIndexOf' (Pattern "bc") 1 (nes (SProxy :: SProxy "abcd")) + { actual: NESCU.lastIndexOf' (Pattern "bc") 1 (nes (Proxy :: Proxy "abcd")) , expected: Just 1 } assertEqual - { actual: NESCU.lastIndexOf' (Pattern "bc") 2 (nes (SProxy :: SProxy "abcd")) + { actual: NESCU.lastIndexOf' (Pattern "bc") 2 (nes (Proxy :: Proxy "abcd")) , expected: Just 1 } assertEqual - { actual: NESCU.lastIndexOf' (Pattern "cb") 0 (nes (SProxy :: SProxy "abcd")) + { actual: NESCU.lastIndexOf' (Pattern "cb") 0 (nes (Proxy :: Proxy "abcd")) , expected: Nothing } log "length" assertEqual - { actual: NESCU.length (nes (SProxy :: SProxy "a")) + { actual: NESCU.length (nes (Proxy :: Proxy "a")) , expected: 1 } assertEqual - { actual: NESCU.length (nes (SProxy :: SProxy "ab")) + { actual: NESCU.length (nes (Proxy :: Proxy "ab")) , expected: 2 } log "take" assertEqual - { actual: NESCU.take 0 (nes (SProxy :: SProxy "ab")) + { actual: NESCU.take 0 (nes (Proxy :: Proxy "ab")) , expected: Nothing } assertEqual - { actual: NESCU.take 1 (nes (SProxy :: SProxy "ab")) - , expected: Just (nes (SProxy :: SProxy "a")) + { actual: NESCU.take 1 (nes (Proxy :: Proxy "ab")) + , expected: Just (nes (Proxy :: Proxy "a")) } assertEqual - { actual: NESCU.take 2 (nes (SProxy :: SProxy "ab")) - , expected: Just (nes (SProxy :: SProxy "ab")) + { actual: NESCU.take 2 (nes (Proxy :: Proxy "ab")) + , expected: Just (nes (Proxy :: Proxy "ab")) } assertEqual - { actual: NESCU.take 3 (nes (SProxy :: SProxy "ab")) - , expected: Just (nes (SProxy :: SProxy "ab")) + { actual: NESCU.take 3 (nes (Proxy :: Proxy "ab")) + , expected: Just (nes (Proxy :: Proxy "ab")) } assertEqual - { actual: NESCU.take (-1) (nes (SProxy :: SProxy "ab")) + { actual: NESCU.take (-1) (nes (Proxy :: Proxy "ab")) , expected: Nothing } log "takeRight" assertEqual - { actual: NESCU.takeRight 0 (nes (SProxy :: SProxy "ab")) + { actual: NESCU.takeRight 0 (nes (Proxy :: Proxy "ab")) , expected: Nothing } assertEqual - { actual: NESCU.takeRight 1 (nes (SProxy :: SProxy "ab")) - , expected: Just (nes (SProxy :: SProxy "b")) + { actual: NESCU.takeRight 1 (nes (Proxy :: Proxy "ab")) + , expected: Just (nes (Proxy :: Proxy "b")) } assertEqual - { actual: NESCU.takeRight 2 (nes (SProxy :: SProxy "ab")) - , expected: Just (nes (SProxy :: SProxy "ab")) + { actual: NESCU.takeRight 2 (nes (Proxy :: Proxy "ab")) + , expected: Just (nes (Proxy :: Proxy "ab")) } assertEqual - { actual: NESCU.takeRight 3 (nes (SProxy :: SProxy "ab")) - , expected: Just (nes (SProxy :: SProxy "ab")) + { actual: NESCU.takeRight 3 (nes (Proxy :: Proxy "ab")) + , expected: Just (nes (Proxy :: Proxy "ab")) } assertEqual - { actual: NESCU.takeRight (-1) (nes (SProxy :: SProxy "ab")) + { actual: NESCU.takeRight (-1) (nes (Proxy :: Proxy "ab")) , expected: Nothing } log "drop" assertEqual - { actual: NESCU.drop 0 (nes (SProxy :: SProxy "ab")) - , expected: Just (nes (SProxy :: SProxy "ab")) + { actual: NESCU.drop 0 (nes (Proxy :: Proxy "ab")) + , expected: Just (nes (Proxy :: Proxy "ab")) } assertEqual - { actual: NESCU.drop 1 (nes (SProxy :: SProxy "ab")) - , expected: Just (nes (SProxy :: SProxy "b")) + { actual: NESCU.drop 1 (nes (Proxy :: Proxy "ab")) + , expected: Just (nes (Proxy :: Proxy "b")) } assertEqual - { actual: NESCU.drop 2 (nes (SProxy :: SProxy "ab")) + { actual: NESCU.drop 2 (nes (Proxy :: Proxy "ab")) , expected: Nothing } assertEqual - { actual: NESCU.drop 3 (nes (SProxy :: SProxy "ab")) + { actual: NESCU.drop 3 (nes (Proxy :: Proxy "ab")) , expected: Nothing } assertEqual - { actual: NESCU.drop (-1) (nes (SProxy :: SProxy "ab")) - , expected: Just (nes (SProxy :: SProxy "ab")) + { actual: NESCU.drop (-1) (nes (Proxy :: Proxy "ab")) + , expected: Just (nes (Proxy :: Proxy "ab")) } log "dropRight" assertEqual - { actual: NESCU.dropRight 0 (nes (SProxy :: SProxy "ab")) - , expected: Just (nes (SProxy :: SProxy "ab")) + { actual: NESCU.dropRight 0 (nes (Proxy :: Proxy "ab")) + , expected: Just (nes (Proxy :: Proxy "ab")) } assertEqual - { actual: NESCU.dropRight 1 (nes (SProxy :: SProxy "ab")) - , expected: Just (nes (SProxy :: SProxy "a")) + { actual: NESCU.dropRight 1 (nes (Proxy :: Proxy "ab")) + , expected: Just (nes (Proxy :: Proxy "a")) } assertEqual - { actual: NESCU.dropRight 2 (nes (SProxy :: SProxy "ab")) + { actual: NESCU.dropRight 2 (nes (Proxy :: Proxy "ab")) , expected: Nothing } assertEqual - { actual: NESCU.dropRight 3 (nes (SProxy :: SProxy "ab")) + { actual: NESCU.dropRight 3 (nes (Proxy :: Proxy "ab")) , expected: Nothing } assertEqual - { actual: NESCU.dropRight (-1) (nes (SProxy :: SProxy "ab")) - , expected: Just (nes (SProxy :: SProxy "ab")) + { actual: NESCU.dropRight (-1) (nes (Proxy :: Proxy "ab")) + , expected: Just (nes (Proxy :: Proxy "ab")) } log "countPrefix" assertEqual - { actual: NESCU.countPrefix (_ == 'a') (nes (SProxy :: SProxy "ab")) + { actual: NESCU.countPrefix (_ == 'a') (nes (Proxy :: Proxy "ab")) , expected: 1 } assertEqual - { actual: NESCU.countPrefix (_ == 'a') (nes (SProxy :: SProxy "aaab")) + { actual: NESCU.countPrefix (_ == 'a') (nes (Proxy :: Proxy "aaab")) , expected: 3 } assertEqual - { actual: NESCU.countPrefix (_ == 'a') (nes (SProxy :: SProxy "abaa")) + { actual: NESCU.countPrefix (_ == 'a') (nes (Proxy :: Proxy "abaa")) , expected: 1 } assertEqual - { actual: NESCU.countPrefix (_ == 'c') (nes (SProxy :: SProxy "abaa")) + { actual: NESCU.countPrefix (_ == 'c') (nes (Proxy :: Proxy "abaa")) , expected: 0 } log "splitAt" assertEqual - { actual: NESCU.splitAt 0 (nes (SProxy :: SProxy "a")) - , expected: { before: Nothing, after: Just (nes (SProxy :: SProxy "a")) } + { actual: NESCU.splitAt 0 (nes (Proxy :: Proxy "a")) + , expected: { before: Nothing, after: Just (nes (Proxy :: Proxy "a")) } } assertEqual - { actual: NESCU.splitAt 1 (nes (SProxy :: SProxy "ab")) - , expected: { before: Just (nes (SProxy :: SProxy "a")), after: Just (nes (SProxy :: SProxy "b")) } + { actual: NESCU.splitAt 1 (nes (Proxy :: Proxy "ab")) + , expected: { before: Just (nes (Proxy :: Proxy "a")), after: Just (nes (Proxy :: Proxy "b")) } } assertEqual - { actual: NESCU.splitAt 3 (nes (SProxy :: SProxy "aabcc")) - , expected: { before: Just (nes (SProxy :: SProxy "aab")), after: Just (nes (SProxy :: SProxy "cc")) } + { actual: NESCU.splitAt 3 (nes (Proxy :: Proxy "aabcc")) + , expected: { before: Just (nes (Proxy :: Proxy "aab")), after: Just (nes (Proxy :: Proxy "cc")) } } assertEqual - { actual: NESCU.splitAt (-1) (nes (SProxy :: SProxy "abc")) - , expected: { before: Nothing, after: Just (nes (SProxy :: SProxy "abc")) } + { actual: NESCU.splitAt (-1) (nes (Proxy :: Proxy "abc")) + , expected: { before: Nothing, after: Just (nes (Proxy :: Proxy "abc")) } } nea :: Array ~> NEA.NonEmptyArray From e897bb707acf3ae512d1dce12d2d79c585a03f72 Mon Sep 17 00:00:00 2001 From: JordanMartinez Date: Thu, 26 Nov 2020 12:30:03 -0800 Subject: [PATCH 163/186] Use coerce instead of unsafeCoerce where appropriate (#130) * Replace unsafeCoerce with safer alternative where possible * Remove unused import * Add docs to the Internal module and NonEmptyString constructor * Remove 'usage of this constructor...' sentence * Move constructor docs to type docs Constructor docs don't yet appear in the generated documentation * Add backticks around NonEmptyString in docs * Add "For internal use only. Do not export" above to/fromNEString & listS --- src/Data/String/NonEmpty/CodePoints.purs | 12 +++++++----- src/Data/String/NonEmpty/CodeUnits.purs | 11 +++++++---- src/Data/String/NonEmpty/Internal.purs | 12 ++++++++++++ 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/Data/String/NonEmpty/CodePoints.purs b/src/Data/String/NonEmpty/CodePoints.purs index 5357851..7b5328a 100644 --- a/src/Data/String/NonEmpty/CodePoints.purs +++ b/src/Data/String/NonEmpty/CodePoints.purs @@ -33,19 +33,21 @@ import Data.Semigroup.Foldable (class Foldable1) import Data.Semigroup.Foldable as F1 import Data.String.CodePoints (CodePoint) import Data.String.CodePoints as CP -import Data.String.NonEmpty.Internal (NonEmptyString, fromString) +import Data.String.NonEmpty.Internal (NonEmptyString(..), fromString) import Data.String.Pattern (Pattern) import Partial.Unsafe (unsafePartial) -import Unsafe.Coerce (unsafeCoerce) +-- For internal use only. Do not export. toNonEmptyString :: String -> NonEmptyString -toNonEmptyString = unsafeCoerce +toNonEmptyString = NonEmptyString +-- For internal use only. Do not export. fromNonEmptyString :: NonEmptyString -> String -fromNonEmptyString = unsafeCoerce +fromNonEmptyString (NonEmptyString s) = s +-- For internal use only. Do not export. liftS :: forall r. (String -> r) -> NonEmptyString -> r -liftS = unsafeCoerce +liftS f (NonEmptyString s) = f s fromCodePointArray :: Array CodePoint -> Maybe NonEmptyString fromCodePointArray = case _ of diff --git a/src/Data/String/NonEmpty/CodeUnits.purs b/src/Data/String/NonEmpty/CodeUnits.purs index 2aa0a6c..d22e180 100644 --- a/src/Data/String/NonEmpty/CodeUnits.purs +++ b/src/Data/String/NonEmpty/CodeUnits.purs @@ -33,20 +33,23 @@ import Data.Maybe (Maybe(..), fromJust) import Data.Semigroup.Foldable (class Foldable1) import Data.Semigroup.Foldable as F1 import Data.String.CodeUnits as CU -import Data.String.NonEmpty.Internal (NonEmptyString, fromString) +import Data.String.NonEmpty.Internal (NonEmptyString(..), fromString) import Data.String.Pattern (Pattern) import Data.String.Unsafe as U import Partial.Unsafe (unsafePartial) import Unsafe.Coerce (unsafeCoerce) +-- For internal use only. Do not export. toNonEmptyString :: String -> NonEmptyString -toNonEmptyString = unsafeCoerce +toNonEmptyString = NonEmptyString +-- For internal use only. Do not export. fromNonEmptyString :: NonEmptyString -> String -fromNonEmptyString = unsafeCoerce +fromNonEmptyString (NonEmptyString s) = s +-- For internal use only. Do not export. liftS :: forall r. (String -> r) -> NonEmptyString -> r -liftS = unsafeCoerce +liftS f (NonEmptyString s) = f s -- | Creates a `NonEmptyString` from a character array `String`, returning -- | `Nothing` if the input is empty. diff --git a/src/Data/String/NonEmpty/Internal.purs b/src/Data/String/NonEmpty/Internal.purs index 7a6197e..707b779 100644 --- a/src/Data/String/NonEmpty/Internal.purs +++ b/src/Data/String/NonEmpty/Internal.purs @@ -1,3 +1,9 @@ +-- | While most of the code in this module is safe, this module does +-- | export a few partial functions and the `NonEmptyString` constructor. +-- | While the partial functions are obvious from the `Partial` constraint in +-- | their type signature, the `NonEmptyString` constructor can be overlooked +-- | when searching for issues in one's code. See the constructor's +-- | documentation for more information. module Data.String.NonEmpty.Internal where import Prelude @@ -13,6 +19,12 @@ import Prim.TypeError as TE import Unsafe.Coerce (unsafeCoerce) -- | A string that is known not to be empty. +-- | +-- | You can use this constructor to create a `NonEmptyString` that isn't +-- | non-empty, breaking the guarantee behind this newtype. It is +-- | provided as an escape hatch mainly for the `Data.NonEmpty.CodeUnits` +-- | and `Data.NonEmpty.CodePoints` modules. Use this at your own risk +-- | when you know what you are doing. newtype NonEmptyString = NonEmptyString String derive newtype instance eqNonEmptyString ∷ Eq NonEmptyString From 52cae45a69c8b4e6c6061e35e677b8079b389a9c Mon Sep 17 00:00:00 2001 From: Emiel van de Laar Date: Wed, 2 Dec 2020 19:46:12 +0100 Subject: [PATCH 164/186] Remove references to `codePointToInt` (#135) The `codePointToInt` function is no longer available but is still referenced in a few documentation examples. Here we replace it with `fromEnum`. --- src/Data/String/CodePoints.purs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 9083c10..82d263c 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -220,7 +220,7 @@ length = Array.length <<< toCodePointArray -- | time linear to the length of the string. -- | -- | ```purescript --- | >>> countPrefix (\c -> codePointToInt c == 0x1D400) "𝐀𝐀 b c 𝐀" +-- | >>> countPrefix (\c -> fromEnum c == 0x1D400) "𝐀𝐀 b c 𝐀" -- | 2 -- | ``` -- | @@ -329,7 +329,7 @@ takeFallback n s = case uncons s of -- | in time linear to the length of the string. -- | -- | ```purescript --- | >>> takeWhile (\c -> codePointToInt c == 0x1D400) "𝐀𝐀 b c 𝐀" +-- | >>> takeWhile (\c -> fromEnum c == 0x1D400) "𝐀𝐀 b c 𝐀" -- | "𝐀𝐀" -- | ``` -- | @@ -356,7 +356,7 @@ drop n s = CU.drop (CU.length (take n s)) s -- | to the length of the string. -- | -- | ```purescript --- | >>> dropWhile (\c -> codePointToInt c == 0x1D400) "𝐀𝐀 b c 𝐀" +-- | >>> dropWhile (\c -> fromEnum c == 0x1D400) "𝐀𝐀 b c 𝐀" -- | " b c 𝐀" -- | ``` -- | From dcbd6cf859b4d7e432c36e49f3bc1048ef206307 Mon Sep 17 00:00:00 2001 From: Thomas Honeyman Date: Sat, 5 Dec 2020 07:33:57 -0800 Subject: [PATCH 165/186] Migrate to GitHub Actions and update installation instructions. (#136) --- .github/workflows/ci.yml | 31 +++++++++++++++++++++++++++++++ .gitignore | 2 +- .travis.yml | 24 ------------------------ README.md | 4 ++-- 4 files changed, 34 insertions(+), 27 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..e2972ba --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,31 @@ +name: CI + +on: push + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - uses: purescript-contrib/setup-purescript@main + with: + purescript: "0.14.0-rc3" + + - uses: actions/setup-node@v1 + with: + node-version: "12" + + - name: Install dependencies + run: | + npm install -g bower + npm install + bower install --production + + - name: Build source + run: npm run-script build + + - name: Run tests + run: | + bower install + npm run-script test --if-present diff --git a/.gitignore b/.gitignore index 332b6cf..b846b63 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ /.* !/.gitignore !/.eslintrc.json -!/.travis.yml +!/.github/ /bower_components/ /node_modules/ /output/ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 116705f..0000000 --- a/.travis.yml +++ /dev/null @@ -1,24 +0,0 @@ -language: node_js -dist: trusty -sudo: required -node_js: stable -env: - - PATH=$HOME/purescript:$PATH -install: - # - TAG=$(basename $(curl --location --silent --output /dev/null -w %{url_effective} https://github.com/purescript/purescript/releases/latest)) - - TAG=v0.14.0-rc3 - - curl --location --output $HOME/purescript.tar.gz https://github.com/purescript/purescript/releases/download/$TAG/linux64.tar.gz - - tar -xvf $HOME/purescript.tar.gz -C $HOME/ - - chmod a+x $HOME/purescript - - npm install -g bower - - npm install -script: - - bower install --production - - npm run -s build - - bower install - - npm -s test -after_success: -- >- - test $TRAVIS_TAG && - echo $GITHUB_TOKEN | pulp login && - echo y | pulp publish --no-push diff --git a/README.md b/README.md index 0565699..62fa5a9 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ # purescript-strings [![Latest release](http://img.shields.io/github/release/purescript/purescript-strings.svg)](https://github.com/purescript/purescript-strings/releases) -[![Build status](https://travis-ci.org/purescript/purescript-strings.svg?branch=master)](https://travis-ci.org/purescript/purescript-strings) +[![Build status](https://github.com/purescript/purescript-strings/workflows/CI/badge.svg?branch=master)](https://github.com/purescript/purescript-strings/actions?query=workflow%3ACI+branch%3Amaster) String and char utility functions, regular expressions. ## Installation ``` -bower install purescript-strings +spago install strings ``` ## Documentation From f1082da572c966e5b965e83d1ab1c40f5fe3b9e6 Mon Sep 17 00:00:00 2001 From: Thomas Honeyman Date: Mon, 7 Dec 2020 20:22:51 -0800 Subject: [PATCH 166/186] Run CI on push / pull_request to master --- .github/workflows/ci.yml | 6 +++++- README.md | 1 + package.json | 4 ++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e2972ba..55efa3d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,10 @@ name: CI -on: push +on: + push: + branches: [master] + pull_request: + branches: [master] jobs: build: diff --git a/README.md b/README.md index 62fa5a9..73292c1 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![Latest release](http://img.shields.io/github/release/purescript/purescript-strings.svg)](https://github.com/purescript/purescript-strings/releases) [![Build status](https://github.com/purescript/purescript-strings/workflows/CI/badge.svg?branch=master)](https://github.com/purescript/purescript-strings/actions?query=workflow%3ACI+branch%3Amaster) +[![Pursuit](https://pursuit.purescript.org/packages/purescript-strings/badge)](https://pursuit.purescript.org/packages/purescript-strings) String and char utility functions, regular expressions. diff --git a/package.json b/package.json index 1f09388..c116cae 100644 --- a/package.json +++ b/package.json @@ -10,9 +10,9 @@ "bench": "npm run bench:build && npm run bench:run" }, "devDependencies": { - "eslint": "^4.19.1", + "eslint": "^7.15.0", "pulp": "^15.0.0", "purescript-psa": "^0.8.0", - "rimraf": "^2.6.2" + "rimraf": "^3.0.2" } } From 7a94736df8575cdcf456c60b1554e652f58bba63 Mon Sep 17 00:00:00 2001 From: Cyril Date: Sat, 19 Dec 2020 02:26:34 +0100 Subject: [PATCH 167/186] Remove the bounds check from the foreign implementation of lastIndexOf' (#137) * Remove the bounds check from the foreign implementation of lastIndexOf' * Update lastIndexOf' documentation --- src/Data/String/CodePoints.purs | 14 +++++++++++++- src/Data/String/CodeUnits.js | 1 - src/Data/String/CodeUnits.purs | 11 +++++++++-- src/Data/String/NonEmpty/CodeUnits.purs | 11 +++++++++-- test/Test/Data/String/CodePoints.purs | 8 ++++++++ test/Test/Data/String/CodeUnits.purs | 4 ++-- test/Test/Data/String/NonEmpty/CodeUnits.purs | 4 ++-- 7 files changed, 43 insertions(+), 10 deletions(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 82d263c..7d0f528 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -286,11 +286,23 @@ lastIndexOf p s = (\i -> length (CU.take i s)) <$> CU.lastIndexOf p s -- | Returns the number of code points preceding the first match of the given -- | pattern in the string. Pattern matches following the given index will be --- | ignored. Returns Nothing when no matches are found. +-- | ignored. +-- | +-- | Giving a negative index is equivalent to giving 0 and giving an index +-- | greater than the number of code points in the string is equivalent to +-- | searching in the whole string. +-- | +-- | Returns Nothing when no matches are found. -- | -- | ```purescript +-- | >>> lastIndexOf' (Pattern "𝐀") (-1) "b 𝐀𝐀 c 𝐀" +-- | Nothing +-- | >>> lastIndexOf' (Pattern "𝐀") 0 "b 𝐀𝐀 c 𝐀" +-- | Nothing -- | >>> lastIndexOf' (Pattern "𝐀") 5 "b 𝐀𝐀 c 𝐀" -- | Just 3 +-- | >>> lastIndexOf' (Pattern "𝐀") 8 "b 𝐀𝐀 c 𝐀" +-- | Just 7 -- | >>> lastIndexOf' (Pattern "o") 5 "b 𝐀𝐀 c 𝐀" -- | Nothing -- | ``` diff --git a/src/Data/String/CodeUnits.js b/src/Data/String/CodeUnits.js index 870b96f..6017fd3 100644 --- a/src/Data/String/CodeUnits.js +++ b/src/Data/String/CodeUnits.js @@ -83,7 +83,6 @@ exports._lastIndexOfStartingAt = 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); }; diff --git a/src/Data/String/CodeUnits.purs b/src/Data/String/CodeUnits.purs index cc919e0..dfa0482 100644 --- a/src/Data/String/CodeUnits.purs +++ b/src/Data/String/CodeUnits.purs @@ -215,14 +215,21 @@ foreign import _lastIndexOf -> Maybe Int -- | Returns the index of the last occurrence of the pattern in the --- | given string, starting at the specified index --- | and searching backwards towards the beginning of the string. +-- | given string, starting at the specified index and searching +-- | backwards towards the beginning of the string. +-- | +-- | Starting at a negative index is equivalent to starting at 0 and +-- | starting at an index greater than the string length is equivalent +-- | to searching in the whole string. +-- | -- | Returns `Nothing` if there is no match. -- | -- | ```purescript +-- | lastIndexOf' (Pattern "a") (-1) "ababa" == Just 0 -- | lastIndexOf' (Pattern "a") 1 "ababa" == Just 0 -- | lastIndexOf' (Pattern "a") 3 "ababa" == Just 2 -- | lastIndexOf' (Pattern "a") 4 "ababa" == Just 4 +-- | lastIndexOf' (Pattern "a") 5 "ababa" == Just 4 -- | ``` -- | lastIndexOf' :: Pattern -> Int -> String -> Maybe Int diff --git a/src/Data/String/NonEmpty/CodeUnits.purs b/src/Data/String/NonEmpty/CodeUnits.purs index d22e180..af3de43 100644 --- a/src/Data/String/NonEmpty/CodeUnits.purs +++ b/src/Data/String/NonEmpty/CodeUnits.purs @@ -159,14 +159,21 @@ lastIndexOf :: Pattern -> NonEmptyString -> Maybe Int lastIndexOf = liftS <<< CU.lastIndexOf -- | Returns the index of the last occurrence of the pattern in the --- | given string, starting at the specified index --- | and searching backwards towards the beginning of the string. +-- | given string, starting at the specified index and searching +-- | backwards towards the beginning of the string. +-- | +-- | Starting at a negative index is equivalent to starting at 0 and +-- | starting at an index greater than the string length is equivalent +-- | to searching in the whole string. +-- | -- | Returns `Nothing` if there is no match. -- | -- | ```purescript +-- | lastIndexOf' (Pattern "a") (-1) (NonEmptyString "ababa") == Just 0 -- | lastIndexOf' (Pattern "a") 1 (NonEmptyString "ababa") == Just 0 -- | lastIndexOf' (Pattern "a") 3 (NonEmptyString "ababa") == Just 2 -- | lastIndexOf' (Pattern "a") 4 (NonEmptyString "ababa") == Just 4 +-- | lastIndexOf' (Pattern "a") 5 (NonEmptyString "ababa") == Just 4 -- | ``` lastIndexOf' :: Pattern -> Int -> NonEmptyString -> Maybe Int lastIndexOf' pat = liftS <<< CU.lastIndexOf' pat diff --git a/test/Test/Data/String/CodePoints.purs b/test/Test/Data/String/CodePoints.purs index 310321b..587ec89 100644 --- a/test/Test/Data/String/CodePoints.purs +++ b/test/Test/Data/String/CodePoints.purs @@ -366,6 +366,10 @@ testStringCodePoints = do { actual: SCP.lastIndexOf' (Pattern str) 1 str , expected: Just 0 } + assertEqual + { actual: SCP.lastIndexOf' (Pattern "a") (-1) str + , expected: Just 0 + } assertEqual { actual: SCP.lastIndexOf' (Pattern "a") 0 str , expected: Just 0 @@ -374,6 +378,10 @@ testStringCodePoints = do { actual: SCP.lastIndexOf' (Pattern "a") 7 str , expected: Just 0 } + assertEqual + { actual: SCP.lastIndexOf' (Pattern "a") (SCP.length str) str + , expected: Just 0 + } assertEqual { actual: SCP.lastIndexOf' (Pattern "z") 0 str , expected: Nothing diff --git a/test/Test/Data/String/CodeUnits.purs b/test/Test/Data/String/CodeUnits.purs index 249a639..9cf010b 100644 --- a/test/Test/Data/String/CodeUnits.purs +++ b/test/Test/Data/String/CodeUnits.purs @@ -270,7 +270,7 @@ testStringCodeUnits = do } assertEqual { actual: SCU.lastIndexOf' (Pattern "") (-1) "ab" - , expected: Nothing + , expected: Just 0 } assertEqual { actual: SCU.lastIndexOf' (Pattern "") 0 "ab" @@ -286,7 +286,7 @@ testStringCodeUnits = do } assertEqual { actual: SCU.lastIndexOf' (Pattern "") 3 "ab" - , expected: Nothing + , expected: Just 2 } assertEqual { actual: SCU.lastIndexOf' (Pattern "bc") 0 "abcd" diff --git a/test/Test/Data/String/NonEmpty/CodeUnits.purs b/test/Test/Data/String/NonEmpty/CodeUnits.purs index 0e78ee8..9a33b0c 100644 --- a/test/Test/Data/String/NonEmpty/CodeUnits.purs +++ b/test/Test/Data/String/NonEmpty/CodeUnits.purs @@ -277,7 +277,7 @@ testNonEmptyStringCodeUnits = do log "lastIndexOf'" assertEqual { actual: NESCU.lastIndexOf' (Pattern "") (-1) (nes (Proxy :: Proxy "ab")) - , expected: Nothing + , expected: Just 0 } assertEqual { actual: NESCU.lastIndexOf' (Pattern "") 0 (nes (Proxy :: Proxy "ab")) @@ -293,7 +293,7 @@ testNonEmptyStringCodeUnits = do } assertEqual { actual: NESCU.lastIndexOf' (Pattern "") 3 (nes (Proxy :: Proxy "ab")) - , expected: Nothing + , expected: Just 2 } assertEqual { actual: NESCU.lastIndexOf' (Pattern "bc") 0 (nes (Proxy :: Proxy "abcd")) From 3de404e1b99e07f1b1d648981879a16fcfff370d Mon Sep 17 00:00:00 2001 From: fujisawa Date: Thu, 24 Dec 2020 06:21:00 +0900 Subject: [PATCH 168/186] Add dotAll regexp flag (#133) --- src/Data/String/Regex.js | 1 + src/Data/String/Regex.purs | 2 ++ src/Data/String/Regex/Flags.purs | 21 +++++++++++++++++++++ test/Test/Data/String/Regex.purs | 3 ++- 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/Data/String/Regex.js b/src/Data/String/Regex.js index ae40c11..8a3a473 100644 --- a/src/Data/String/Regex.js +++ b/src/Data/String/Regex.js @@ -27,6 +27,7 @@ exports.flagsImpl = function (r) { multiline: r.multiline, ignoreCase: r.ignoreCase, global: r.global, + dotAll: r.dotAll, sticky: !!r.sticky, unicode: !!r.unicode }; diff --git a/src/Data/String/Regex.purs b/src/Data/String/Regex.purs index 4f2e74f..43ebf21 100644 --- a/src/Data/String/Regex.purs +++ b/src/Data/String/Regex.purs @@ -61,6 +61,7 @@ renderFlags (RegexFlags f) = (if f.global then "g" else "") <> (if f.ignoreCase then "i" else "") <> (if f.multiline then "m" else "") <> + (if f.dotAll then "s" else "") <> (if f.sticky then "y" else "") <> (if f.unicode then "u" else "") @@ -70,6 +71,7 @@ parseFlags s = RegexFlags { global: contains (Pattern "g") s , ignoreCase: contains (Pattern "i") s , multiline: contains (Pattern "m") s + , dotAll: contains (Pattern "s") s , sticky: contains (Pattern "y") s , unicode: contains (Pattern "u") s } diff --git a/src/Data/String/Regex/Flags.purs b/src/Data/String/Regex/Flags.purs index bd14d8c..dc7a0c7 100644 --- a/src/Data/String/Regex/Flags.purs +++ b/src/Data/String/Regex/Flags.purs @@ -9,6 +9,7 @@ type RegexFlagsRec = { global :: Boolean , ignoreCase :: Boolean , multiline :: Boolean + , dotAll :: Boolean , sticky :: Boolean , unicode :: Boolean } @@ -22,6 +23,7 @@ noFlags = RegexFlags { global: false , ignoreCase: false , multiline: false + , dotAll : false , sticky: false , unicode: false } @@ -32,6 +34,7 @@ global = RegexFlags { global: true , ignoreCase: false , multiline: false + , dotAll : false , sticky: false , unicode: false } @@ -42,6 +45,7 @@ ignoreCase = RegexFlags { global: false , ignoreCase: true , multiline: false + , dotAll : false , sticky: false , unicode: false } @@ -52,6 +56,7 @@ multiline = RegexFlags { global: false , ignoreCase: false , multiline: true + , dotAll : false , sticky: false , unicode: false } @@ -62,6 +67,7 @@ sticky = RegexFlags { global: false , ignoreCase: false , multiline: false + , dotAll : false , sticky: true , unicode: false } @@ -72,15 +78,28 @@ unicode = RegexFlags { global: false , ignoreCase: false , multiline: false + , dotAll : false , sticky: false , unicode: true } +-- | Only dotAll flag set to true +dotAll :: RegexFlags +dotAll = RegexFlags + { global: false + , ignoreCase: false + , multiline: false + , dotAll : true + , sticky: false + , unicode: false + } + instance semigroupRegexFlags :: Semigroup RegexFlags where append (RegexFlags x) (RegexFlags y) = RegexFlags { global: x.global || y.global , ignoreCase: x.ignoreCase || y.ignoreCase , multiline: x.multiline || y.multiline + , dotAll: x.dotAll || y.dotAll , sticky: x.sticky || y.sticky , unicode: x.unicode || y.unicode } @@ -93,6 +112,7 @@ instance eqRegexFlags :: Eq RegexFlags where = x.global == y.global && x.ignoreCase == y.ignoreCase && x.multiline == y.multiline + && x.dotAll == y.dotAll && x.sticky == y.sticky && x.unicode == y.unicode @@ -104,6 +124,7 @@ instance showRegexFlags :: Show RegexFlags where <> (guard flags.global $> "global") <> (guard flags.ignoreCase $> "ignoreCase") <> (guard flags.multiline $> "multiline") + <> (guard flags.dotAll $> "dotAll") <> (guard flags.sticky $> "sticky") <> (guard flags.unicode $> "unicode") in diff --git a/test/Test/Data/String/Regex.purs b/test/Test/Data/String/Regex.purs index 326b35e..a458759 100644 --- a/test/Test/Data/String/Regex.purs +++ b/test/Test/Data/String/Regex.purs @@ -5,7 +5,7 @@ import Data.String.Regex import Data.Array.NonEmpty (NonEmptyArray, fromArray) import Data.Either (isLeft) import Data.Maybe (Maybe(..), fromJust) -import Data.String.Regex.Flags (global, ignoreCase, noFlags) +import Data.String.Regex.Flags (dotAll, global, ignoreCase, noFlags) import Data.String.Regex.Unsafe (unsafeRegex) import Effect (Effect) import Effect.Console (log) @@ -24,6 +24,7 @@ testStringRegex = do assert $ "quxbarfoobaz" == replace (unsafeRegex "foo" noFlags) "qux" "foobarfoobaz" assert $ "quxbarquxbaz" == replace (unsafeRegex "foo" global) "qux" "foobarfoobaz" assert $ "quxbarquxbaz" == replace (unsafeRegex "foo" (global <> ignoreCase)) "qux" "foobarFOObaz" + assert $ "quxbarfoobaz" == replace (unsafeRegex ".foo" dotAll) "qux" "\nfoobarfoobaz" log "match" assert $ match (unsafeRegex "^abc$" noFlags) "abc" == Just (nea [Just "abc"]) From 5815ea91fafa623d99d703335d4645da748faa1b Mon Sep 17 00:00:00 2001 From: David Chambers Date: Wed, 6 Jan 2021 16:49:07 +0100 Subject: [PATCH 169/186] update replace' to reflect the existence of optional capturing groups (#126) * Update CI to use v0.14.0-rc5 * update replace' to reflect the existence of optional capturing groups Co-authored-by: Jordan Martinez --- .github/workflows/ci.yml | 2 +- src/Data/String/Regex.js | 21 +++++++++++++++------ src/Data/String/Regex.purs | 25 ++++++++++++++++--------- test/Test/Data/String/Regex.purs | 6 +++++- 4 files changed, 37 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 55efa3d..f4f44e5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: - uses: purescript-contrib/setup-purescript@main with: - purescript: "0.14.0-rc3" + purescript: "0.14.0-rc5" - uses: actions/setup-node@v1 with: diff --git a/src/Data/String/Regex.js b/src/Data/String/Regex.js index 8a3a473..b3be593 100644 --- a/src/Data/String/Regex.js +++ b/src/Data/String/Regex.js @@ -68,12 +68,21 @@ exports.replace = function (r) { }; }; -exports.replaceBy = function (r) { - return function (f) { - return function (s2) { - return s2.replace(r, function (match) { - return f(match)(Array.prototype.splice.call(arguments, 1, arguments.length - 3)); - }); +exports._replaceBy = function (just) { + return function (nothing) { + return function (r) { + return function (f) { + return function (s) { + return s.replace(r, function (match) { + var groups = []; + var group, i = 1; + while (typeof (group = arguments[i++]) !== "number") { + groups.push(group == null ? nothing : just(group)); + } + return f(match)(groups); + }); + }; + }; }; }; }; diff --git a/src/Data/String/Regex.purs b/src/Data/String/Regex.purs index 43ebf21..aae56e1 100644 --- a/src/Data/String/Regex.purs +++ b/src/Data/String/Regex.purs @@ -1,5 +1,5 @@ -- | Wraps Javascript's `RegExp` object that enables matching strings with --- | patternes defined by regular expressions. +-- | patterns defined by regular expressions. -- | For details of the underlying implementation, see [RegExp Reference at MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp). module Data.String.Regex ( Regex(..) @@ -95,18 +95,25 @@ foreign import _match match :: Regex -> String -> Maybe (NonEmptyArray (Maybe String)) match = _match Just Nothing --- | Replaces occurences of the `Regex` with the first string. The replacement +-- | Replaces occurrences of the `Regex` with the first string. The replacement -- | string can include special replacement patterns escaped with `"$"`. -- | See [reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace). foreign import replace :: Regex -> String -> String -> String --- | Transforms occurences of the `Regex` using a function of the matched --- | substring and a list of submatch strings. --- | See the [reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_function_as_a_parameter). -replace' :: Regex -> (String -> Array String -> String) -> String -> String -replace' = replaceBy +foreign import _replaceBy + :: (forall r. r -> Maybe r) + -> (forall r. Maybe r) + -> Regex + -> (String -> Array (Maybe String) -> String) + -> String + -> String -foreign import replaceBy :: Regex -> (String -> Array String -> String) -> String -> String +-- | Transforms occurrences of the `Regex` using a function of the matched +-- | substring and a list of captured substrings of type `Maybe String`, +-- | where `Nothing` represents an unmatched optional capturing group. +-- | See the [reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_function_as_a_parameter). +replace' :: Regex -> (String -> Array (Maybe String) -> String) -> String -> String +replace' = _replaceBy Just Nothing foreign import _search :: (forall r. r -> Maybe r) @@ -120,5 +127,5 @@ foreign import _search search :: Regex -> String -> Maybe Int search = _search Just Nothing --- | Split the string into an array of substrings along occurences of the `Regex`. +-- | Split the string into an array of substrings along occurrences of the `Regex`. foreign import split :: Regex -> String -> Array String diff --git a/test/Test/Data/String/Regex.purs b/test/Test/Data/String/Regex.purs index a458759..01d583b 100644 --- a/test/Test/Data/String/Regex.purs +++ b/test/Test/Data/String/Regex.purs @@ -10,7 +10,7 @@ import Data.String.Regex.Unsafe (unsafeRegex) import Effect (Effect) import Effect.Console (log) import Partial.Unsafe (unsafePartial) -import Prelude (type (~>), Unit, discard, not, ($), (<<<), (<>), (==)) +import Prelude (type (~>), Unit, discard, not, show, ($), (<<<), (<>), (==)) import Test.Assert (assert) testStringRegex :: Effect Unit @@ -35,6 +35,10 @@ testStringRegex = do log "replace'" assert $ replace' (unsafeRegex "-" noFlags) (\s xs -> "!") "a-b-c" == "a!b-c" + assert $ replace' (unsafeRegex "(foo)(bar)?" noFlags) (\s xs -> show xs) "<>" == "<>" + assert $ replace' (unsafeRegex "(foo)(bar)?" noFlags) (\s xs -> show xs) "" == "<[(Just \"foo\"),Nothing]>" + assert $ replace' (unsafeRegex "(foo)(bar)?" noFlags) (\s xs -> show xs) "" == "<[(Just \"foo\"),(Just \"bar\")]>" + assert $ replace' (unsafeRegex "@(?\\w+)" noFlags) (\s xs -> show xs) "@purescript" == "[(Just \"purescript\")]" log "search" assert $ search (unsafeRegex "b" noFlags) "abc" == Just 1 From 6e27e114ac2b1672c730ba438826f0dec47ea300 Mon Sep 17 00:00:00 2001 From: JordanMartinez Date: Sun, 10 Jan 2021 15:00:08 -0800 Subject: [PATCH 170/186] Generate changelog and add PR template (#140) * Generate CHANGELOG.md file using notes from previous GH releases * Add pull request template --- .github/PULL_REQUEST_TEMPLATE.md | 12 ++ CHANGELOG.md | 228 +++++++++++++++++++++++++++++++ 2 files changed, 240 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 CHANGELOG.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..4435abb --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,12 @@ +**Description of the change** + +Clearly and concisely describe the purpose of the pull request. If this PR relates to an existing issue or change proposal, please link to it. Include any other background context that would help reviewers understand the motivation for this PR. + +--- + +**Checklist:** + +- [ ] Added the change to the changelog's "Unreleased" section with a reference to this PR (e.g. "- Made a change (#0000)") +- [ ] Linked any existing issues or proposals that this pull request should close +- [ ] Updated or added relevant documentation +- [ ] Added a test for the contribution (if applicable) diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..8cf568f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,228 @@ +# Changelog + +Notable changes to this project are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +Breaking changes: + +New features: + +Bugfixes: + +Other improvements: + +## [v4.0.2](https://github.com/purescript/purescript-strings/releases/tag/v4.0.2) - 2020-05-13 + +- Improved performance for `stripPrefix` / `stripSuffix` (#123, @michaelficarra) + +## [v4.0.1](https://github.com/purescript/purescript-strings/releases/tag/v4.0.1) - 2018-11-11 + +- Fixed out of bounds access in `unsafeCodePointAt0Fallback` (@zyla) +- Fixed `slice` when end index equals string length (@abaco) + +## [v4.0.0](https://github.com/purescript/purescript-strings/releases/tag/v4.0.0) - 2018-05-23 + +- Updated for PureScript 0.12 +- `splitAt` now always returns a value (#78, @MonoidMusician) +- Added `slice` (@themattchan) +- Added more `String` `Gen`s to correspond with `Char` `Gen`s (@matthewleon) +- `Regex` `match` now returns `NonEmptyArray` +- All string functions now operate on code points now rather than code units. The old functions are available via the `.CodeUnits` modules +- `fromCharCode` can return `Nothing` now if given a value out of range + +## [v3.5.0](https://github.com/purescript/purescript-strings/releases/tag/v3.5.0) - 2018-02-12 + +- Added `Data.String.NonEmpty` + +## [v3.4.0](https://github.com/purescript/purescript-strings/releases/tag/v3.4.0) - 2017-12-28 + +* Add `Show CodePoint` instance (@csicar) +* Add `codePointFromChar` (@csicar) +* Expanded docs for most functions in `Data.String` and `Data.String.CodePoints` (@csicar) + +## [v3.3.2](https://github.com/purescript/purescript-strings/releases/tag/v3.3.2) - 2017-11-19 + +Performance improvement in `Data.String.Regex.match` (@fehrenbach) + +## [v3.3.1](https://github.com/purescript/purescript-strings/releases/tag/v3.3.1) - 2017-08-06 + +Fix some `Show` instances (@Rufflewind) + +## [v3.3.0](https://github.com/purescript/purescript-strings/releases/tag/v3.3.0) - 2017-07-10 + +* Add a new module `Data.String.CodePoints`, which treats strings as sequences of Unicode code points rather than sequences of UTF-16 code units. In the future we may swap this module with `Data.String`. (@michaelficarra) +* Fix a typo in the documentation (@ijks) + +## [v3.2.1](https://github.com/purescript/purescript-strings/releases/tag/v3.2.1) - 2017-06-06 + +- Ensure `genString` behaves the same regardless of the `MonadGen` implementation of `chooseInt` when `max < min` + +## [v3.2.0](https://github.com/purescript/purescript-strings/releases/tag/v3.2.0) - 2017-06-05 + +- Generated strings from `genString` now vary in length +- Added additional `Char` generators + +## [v3.1.0](https://github.com/purescript/purescript-strings/releases/tag/v3.1.0) - 2017-04-28 + +- Added some generator functions - introduced `Data.String.Gen` and `Data.Char.Gen` + +## [v3.0.0](https://github.com/purescript/purescript-strings/releases/tag/v3.0.0) - 2017-03-26 + +- Updated for PureScript 0.11 + +## [v2.1.0](https://github.com/purescript/purescript-strings/releases/tag/v2.1.0) - 2016-12-25 + +Add `unsafeRegex` (@rightfold) + +## [v2.0.2](https://github.com/purescript/purescript-strings/releases/tag/v2.0.2) - 2016-10-26 + +- Documentation fix for `split` #70 (@leighman) + +## [v2.0.1](https://github.com/purescript/purescript-strings/releases/tag/v2.0.1) - 2016-10-08 + +- Improved `null` check implementation (@Risto-Stevcev) + +## [v2.0.0](https://github.com/purescript/purescript-strings/releases/tag/v2.0.0) - 2016-10-08 + +- Updated dependencies +- `Pattern` and `Replacement` newtypes are now used to distinguish between arguments when a function accepts multiple strings +- `RegexFlags` have been reworked as a monoid (@Risto-Stevcev) + +## [v1.1.0](https://github.com/purescript/purescript-strings/releases/tag/v1.1.0) - 2016-07-20 + +- Restored export of the `count` function. + +## [v1.0.0](https://github.com/purescript/purescript-strings/releases/tag/v1.0.0) - 2016-06-01 + +This release is intended for the PureScript 0.9.1 compiler and newer. + +**Note**: The v1.0.0 tag is not meant to indicate the library is β€œfinished”, the core libraries are all being bumped to this for the 0.9 compiler release so as to use semver more correctly. + +## [v1.0.0-rc.2](https://github.com/purescript/purescript-strings/releases/tag/v1.0.0-rc.2) - 2016-05-20 + +- Fix unused import warning + +## [v1.0.0-rc.1](https://github.com/purescript/purescript-strings/releases/tag/v1.0.0-rc.1) - 2016-03-24 + +- Release candidate for the psc 0.8+ core libraries + +## [v0.7.1](https://github.com/purescript/purescript-strings/releases/tag/v0.7.1) - 2015-11-20 + +- Removed unused imports (@tfausak) + +## [v0.7.0](https://github.com/purescript/purescript-strings/releases/tag/v0.7.0) - 2015-08-13 + +- Removed orphan (and incorrect) `Bounded Char` instance + +## [v0.6.0](https://github.com/purescript/purescript-strings/releases/tag/v0.6.0) - 2015-08-02 + +- Added `toLower` and `toUpper` to `Data.Char` +- `search` in `Data.String.Regex` now returns `Maybe` result rather than using -1 for failure +- Added test suite + +All updates by @LiamGoodacre + +## [v0.5.5](https://github.com/purescript/purescript-strings/releases/tag/v0.5.5) - 2015-07-28 + +Add `stripSuffix`. + +## [v0.5.4](https://github.com/purescript/purescript-strings/releases/tag/v0.5.4) - 2015-07-18 + +- Removed duplicate `Show` instance for `Char` (@anttih) + +## [v0.5.3](https://github.com/purescript/purescript-strings/releases/tag/v0.5.3) - 2015-07-10 + +Add `stripPrefix` (@hdgarrood) + +## [v0.5.2](https://github.com/purescript/purescript-strings/releases/tag/v0.5.2) - 2015-07-07 + +- Fixed `char` and `charCodeAt` in `Data.String.Unsafe` #36 (@stkb) + +## [v0.5.1](https://github.com/purescript/purescript-strings/releases/tag/v0.5.1) - 2015-07-06 + +- Fixed missing `count` implementation (@qxjit) + +## [v0.5.0](https://github.com/purescript/purescript-strings/releases/tag/v0.5.0) - 2015-06-30 + +This release works with versions 0.7.\* of the PureScript compiler. It will not work with older versions. If you are using an older version, you should require an older, compatible version of this library. + +## [v0.5.0-rc.3](https://github.com/purescript/purescript-strings/releases/tag/v0.5.0-rc.3) - 2015-06-12 + +- Fixed various FFI exports (@sharkdp) + +## [v0.5.0-rc.2](https://github.com/purescript/purescript-strings/releases/tag/v0.5.0-rc.2) - 2015-06-07 + +Fix `localeCompare` + +## [v0.5.0-rc.1](https://github.com/purescript/purescript-strings/releases/tag/v0.5.0-rc.1) - 2015-06-07 + +Initial release candidate of the library intended for the 0.7 compiler. + +## [v0.4.5](https://github.com/purescript/purescript-strings/releases/tag/v0.4.5) - 2015-03-23 + +- Added `char` to `Data.String.Unsafe` (@brainrape) +- Functions in `Data.String.Unsafe` now throw errors immediately when given unacceptable inputs (@brainrape) + +## [v0.4.4](https://github.com/purescript/purescript-strings/releases/tag/v0.4.4) - 2015-03-22 + +Updated docs + +## [v0.4.3](https://github.com/purescript/purescript-strings/releases/tag/v0.4.3) - 2015-02-18 + +- Added `noFlags` record for default regex flags (@fresheyeball) + +## [v0.4.2](https://github.com/purescript/purescript-strings/releases/tag/v0.4.2) - 2014-11-28 + + + +## [v0.4.1](https://github.com/purescript/purescript-strings/releases/tag/v0.4.1) - 2014-11-06 + + + +## [v0.4.0](https://github.com/purescript/purescript-strings/releases/tag/v0.4.0) - 2014-10-27 + +- Made `charCodeAt` safe, added unsafe versions of `charAt`, `charCodeAt` (@garyb) + +## [v0.3.3](https://github.com/purescript/purescript-strings/releases/tag/v0.3.3) - 2014-10-24 + +- Added `split` to `Data.String.Regex` (@davidchambers) + +## [v0.3.2](https://github.com/purescript/purescript-strings/releases/tag/v0.3.2) - 2014-10-16 + + + +## [v0.3.1](https://github.com/purescript/purescript-strings/releases/tag/v0.3.1) - 2014-10-15 + + + +## [v0.3.0](https://github.com/purescript/purescript-strings/releases/tag/v0.3.0) - 2014-10-14 + +- Introduced `Char` newtype and corresponding functions (@jdegoes) +- Made `charAt` safe - breaking change (@jdegoes) + +## [v0.2.1](https://github.com/purescript/purescript-strings/releases/tag/v0.2.1) - 2014-07-21 + +- Fix typo in FFI definition for `flags` (@garyb) + +## [v0.2.0](https://github.com/purescript/purescript-strings/releases/tag/v0.2.0) - 2014-07-20 + +- `Show` instance for `Regex` (@michaelficarra) +- `Regex` now has `RegexFlags` rather than a string for options (@michaelficarra) + +## [v0.1.3](https://github.com/purescript/purescript-strings/releases/tag/v0.1.3) - 2014-05-04 + +Renamed `Data.String.Regex.replaceR` to `replace`, added `replace'` which uses a function to construct replacements for matches. + +## [v0.1.2](https://github.com/purescript/purescript-strings/releases/tag/v0.1.2) - 2014-04-30 + +Added `indexOf'` and `lastIndexOf'` (paf31) + +## [v0.1.1](https://github.com/purescript/purescript-strings/releases/tag/v0.1.1) - 2014-04-27 + + + +## [v0.1.0](https://github.com/purescript/purescript-strings/releases/tag/v0.1.0) - 2014-04-25 + + + From dd35f96ca15bea479e130fda2a15a5b2d6b2575d Mon Sep 17 00:00:00 2001 From: Thomas Honeyman Date: Wed, 20 Jan 2021 04:00:38 -0800 Subject: [PATCH 171/186] Update changelog since v4.0.2 (#141) --- CHANGELOG.md | 64 +++++++++++++++++++++++----------------------------- 1 file changed, 28 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cf568f..0bacb51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,22 @@ Notable changes to this project are documented in this file. The format is based ## [Unreleased] Breaking changes: +- Added support for PureScript 0.14 and dropped support for all previous versions (#129) +- Updated `replace'` to reflect the existence of optional capturing groups (#126) New features: +- Replaced `unsafeCoerce` with `coerce` where appropriate (#130) +- Replaced monomorphic proxies with `Type.Proxy.Proxy` and polymorphic variables (#134) +- Added a dotAll regexp flag (#133) Bugfixes: +- Removed the bounds check from the foreign implementation of `lastIndexOf'` (#137) Other improvements: +- Fix line endings to match overall project style (#132) +- Removed references to `codePointToInt`, which no longer exists (#135) +- Migrated CI to GitHub Actions and updated installation instructions to use Spago (#136) +- Added a changelog and pull request template (#140, #141) ## [v4.0.2](https://github.com/purescript/purescript-strings/releases/tag/v4.0.2) - 2020-05-13 @@ -37,22 +47,22 @@ Other improvements: ## [v3.4.0](https://github.com/purescript/purescript-strings/releases/tag/v3.4.0) - 2017-12-28 -* Add `Show CodePoint` instance (@csicar) -* Add `codePointFromChar` (@csicar) -* Expanded docs for most functions in `Data.String` and `Data.String.CodePoints` (@csicar) +- Add `Show CodePoint` instance (@csicar) +- Add `codePointFromChar` (@csicar) +- Expanded docs for most functions in `Data.String` and `Data.String.CodePoints` (@csicar) ## [v3.3.2](https://github.com/purescript/purescript-strings/releases/tag/v3.3.2) - 2017-11-19 -Performance improvement in `Data.String.Regex.match` (@fehrenbach) +- Performance improvement in `Data.String.Regex.match` (@fehrenbach) ## [v3.3.1](https://github.com/purescript/purescript-strings/releases/tag/v3.3.1) - 2017-08-06 -Fix some `Show` instances (@Rufflewind) +- Fix some `Show` instances (@Rufflewind) ## [v3.3.0](https://github.com/purescript/purescript-strings/releases/tag/v3.3.0) - 2017-07-10 -* Add a new module `Data.String.CodePoints`, which treats strings as sequences of Unicode code points rather than sequences of UTF-16 code units. In the future we may swap this module with `Data.String`. (@michaelficarra) -* Fix a typo in the documentation (@ijks) +- Add a new module `Data.String.CodePoints`, which treats strings as sequences of Unicode code points rather than sequences of UTF-16 code units. In the future we may swap this module with `Data.String`. (@michaelficarra) +- Fix a typo in the documentation (@ijks) ## [v3.2.1](https://github.com/purescript/purescript-strings/releases/tag/v3.2.1) - 2017-06-06 @@ -73,7 +83,7 @@ Fix some `Show` instances (@Rufflewind) ## [v2.1.0](https://github.com/purescript/purescript-strings/releases/tag/v2.1.0) - 2016-12-25 -Add `unsafeRegex` (@rightfold) +- Added `unsafeRegex` (@rightfold) ## [v2.0.2](https://github.com/purescript/purescript-strings/releases/tag/v2.0.2) - 2016-10-26 @@ -99,14 +109,6 @@ This release is intended for the PureScript 0.9.1 compiler and newer. **Note**: The v1.0.0 tag is not meant to indicate the library is β€œfinished”, the core libraries are all being bumped to this for the 0.9 compiler release so as to use semver more correctly. -## [v1.0.0-rc.2](https://github.com/purescript/purescript-strings/releases/tag/v1.0.0-rc.2) - 2016-05-20 - -- Fix unused import warning - -## [v1.0.0-rc.1](https://github.com/purescript/purescript-strings/releases/tag/v1.0.0-rc.1) - 2016-03-24 - -- Release candidate for the psc 0.8+ core libraries - ## [v0.7.1](https://github.com/purescript/purescript-strings/releases/tag/v0.7.1) - 2015-11-20 - Removed unused imports (@tfausak) @@ -147,17 +149,8 @@ Add `stripPrefix` (@hdgarrood) This release works with versions 0.7.\* of the PureScript compiler. It will not work with older versions. If you are using an older version, you should require an older, compatible version of this library. -## [v0.5.0-rc.3](https://github.com/purescript/purescript-strings/releases/tag/v0.5.0-rc.3) - 2015-06-12 - - Fixed various FFI exports (@sharkdp) - -## [v0.5.0-rc.2](https://github.com/purescript/purescript-strings/releases/tag/v0.5.0-rc.2) - 2015-06-07 - -Fix `localeCompare` - -## [v0.5.0-rc.1](https://github.com/purescript/purescript-strings/releases/tag/v0.5.0-rc.1) - 2015-06-07 - -Initial release candidate of the library intended for the 0.7 compiler. +- Fixed `localeCompare` ## [v0.4.5](https://github.com/purescript/purescript-strings/releases/tag/v0.4.5) - 2015-03-23 @@ -166,7 +159,7 @@ Initial release candidate of the library intended for the 0.7 compiler. ## [v0.4.4](https://github.com/purescript/purescript-strings/releases/tag/v0.4.4) - 2015-03-22 -Updated docs +- Updated docs ## [v0.4.3](https://github.com/purescript/purescript-strings/releases/tag/v0.4.3) - 2015-02-18 @@ -174,11 +167,11 @@ Updated docs ## [v0.4.2](https://github.com/purescript/purescript-strings/releases/tag/v0.4.2) - 2014-11-28 - +- Added `null`, `singleton`, `uncons`, `takeWhile`, and `dropWhile` to `Data.String` (@NightRa) ## [v0.4.1](https://github.com/purescript/purescript-strings/releases/tag/v0.4.1) - 2014-11-06 - +- Use ternary operator in JavaScript output (@davidchambers) ## [v0.4.0](https://github.com/purescript/purescript-strings/releases/tag/v0.4.0) - 2014-10-27 @@ -190,11 +183,11 @@ Updated docs ## [v0.3.2](https://github.com/purescript/purescript-strings/releases/tag/v0.3.2) - 2014-10-16 - +- Added essential instances for `Char` (@jdegoes) ## [v0.3.1](https://github.com/purescript/purescript-strings/releases/tag/v0.3.1) - 2014-10-15 - +- Fixed typo in `fromCharArray` FFI implementation (@jdegoes) ## [v0.3.0](https://github.com/purescript/purescript-strings/releases/tag/v0.3.0) - 2014-10-14 @@ -212,17 +205,16 @@ Updated docs ## [v0.1.3](https://github.com/purescript/purescript-strings/releases/tag/v0.1.3) - 2014-05-04 -Renamed `Data.String.Regex.replaceR` to `replace`, added `replace'` which uses a function to construct replacements for matches. +- Renamed `Data.String.Regex.replaceR` to `replace`, added `replace'` which uses a function to construct replacements for matches. ## [v0.1.2](https://github.com/purescript/purescript-strings/releases/tag/v0.1.2) - 2014-04-30 -Added `indexOf'` and `lastIndexOf'` (paf31) +- Added `indexOf'` and `lastIndexOf'` (paf31) ## [v0.1.1](https://github.com/purescript/purescript-strings/releases/tag/v0.1.1) - 2014-04-27 - +- Swapped `joinWith` arguments for better style ## [v0.1.0](https://github.com/purescript/purescript-strings/releases/tag/v0.1.0) - 2014-04-25 - - +- Initial release From 157e372a23e4becd594d7e7bff6f372a6f63dd82 Mon Sep 17 00:00:00 2001 From: Cyril Date: Fri, 26 Feb 2021 19:55:44 +0100 Subject: [PATCH 172/186] Prepare v5.0.0 release (#144) * Update CI to build with the latest version of the compiler * Update the bower repository URL to match the URL in the registry * Upgrade bower dependencies * Update the changelog --- .github/workflows/ci.yml | 2 -- CHANGELOG.md | 10 ++++++++++ bower.json | 40 ++++++++++++++++++++-------------------- 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f4f44e5..43d2897 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,8 +13,6 @@ jobs: - uses: actions/checkout@v2 - uses: purescript-contrib/setup-purescript@main - with: - purescript: "0.14.0-rc5" - uses: actions/setup-node@v1 with: diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bacb51..311b72b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ Notable changes to this project are documented in this file. The format is based ## [Unreleased] +Breaking changes: + +New features: + +Bugfixes: + +Other improvements: + +## [v5.0.0](https://github.com/purescript/purescript-strings/releases/tag/v5.0.0) - 2021-02-26 + Breaking changes: - Added support for PureScript 0.14 and dropped support for all previous versions (#129) - Updated `replace'` to reflect the existence of optional capturing groups (#126) diff --git a/bower.json b/bower.json index e401464..d4c9f36 100644 --- a/bower.json +++ b/bower.json @@ -4,7 +4,7 @@ "license": "BSD-3-Clause", "repository": { "type": "git", - "url": "git://github.com/purescript/purescript-strings.git" + "url": "https://github.com/purescript/purescript-strings.git" }, "ignore": [ "**/.*", @@ -16,26 +16,26 @@ "package.json" ], "dependencies": { - "purescript-arrays": "master", - "purescript-control": "master", - "purescript-either": "master", - "purescript-enums": "master", - "purescript-foldable-traversable": "master", - "purescript-gen": "master", - "purescript-integers": "master", - "purescript-maybe": "master", - "purescript-newtype": "master", - "purescript-nonempty": "master", - "purescript-partial": "master", - "purescript-prelude": "master", - "purescript-tailrec": "master", - "purescript-tuples": "master", - "purescript-unfoldable": "master", - "purescript-unsafe-coerce": "master" + "purescript-arrays": "^6.0.0", + "purescript-control": "^5.0.0", + "purescript-either": "^5.0.0", + "purescript-enums": "^5.0.0", + "purescript-foldable-traversable": "^5.0.0", + "purescript-gen": "^3.0.0", + "purescript-integers": "^5.0.0", + "purescript-maybe": "^5.0.0", + "purescript-newtype": "^4.0.0", + "purescript-nonempty": "^6.0.0", + "purescript-partial": "^3.0.0", + "purescript-prelude": "^5.0.0", + "purescript-tailrec": "^5.0.0", + "purescript-tuples": "^6.0.0", + "purescript-unfoldable": "^5.0.0", + "purescript-unsafe-coerce": "^5.0.0" }, "devDependencies": { - "purescript-assert": "master", - "purescript-console": "master", - "purescript-minibench": "master" + "purescript-assert": "^5.0.0", + "purescript-console": "^5.0.0", + "purescript-minibench": "^3.0.0" } } From 36493eee748bc35e2d4ecb09992f3c3eae42f4e8 Mon Sep 17 00:00:00 2001 From: Mohammed Anas <6daf084a-8eaf-40fb-86c7-8500077c3b69@anonaddy.me> Date: Thu, 22 Apr 2021 19:26:30 +0000 Subject: [PATCH 173/186] Use backticks around PS code in docs (#148) * Use backticks around PS code in docs * Add change to CHANGELOG.md --- CHANGELOG.md | 1 + src/Data/String/CodePoints.purs | 14 +++++++------- src/Data/String/CodeUnits.purs | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 311b72b..c3bf386 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ New features: Bugfixes: Other improvements: +- Surround code with backticks in documentation (#148) ## [v5.0.0](https://github.com/purescript/purescript-strings/releases/tag/v5.0.0) - 2021-02-26 diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 7d0f528..65e0b55 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -42,7 +42,7 @@ import Data.String.Unsafe as Unsafe import Data.Tuple (Tuple(..)) import Data.Unfoldable (unfoldr) --- | CodePoint is an Int bounded between 0 and 0x10FFFF, corresponding to +-- | CodePoint is an `Int` bounded between `0` and `0x10FFFF`, corresponding to -- | Unicode code points. newtype CodePoint = CodePoint Int @@ -67,7 +67,7 @@ instance boundedEnumCodePoint :: BoundedEnum CodePoint where | n >= 0 && n <= 0x10FFFF = Just (CodePoint n) | otherwise = Nothing --- | Creates a CodePoint from a given Char. +-- | Creates a `CodePoint` from a given `Char`. -- | -- | ```purescript -- | >>> codePointFromChar 'B' @@ -178,7 +178,7 @@ codePointAtFallback n s = case uncons s of _ -> Nothing -- | Returns a record with the first code point and the remaining code points --- | of the string. Returns Nothing if the string is empty. Operates in +-- | of the string. Returns `Nothing` if the string is empty. Operates in -- | constant space and time. -- | -- | ```purescript @@ -243,7 +243,7 @@ countTail p s accum = case uncons s of _ -> accum -- | Returns the number of code points preceding the first match of the given --- | pattern in the string. Returns Nothing when no matches are found. +-- | pattern in the string. Returns `Nothing` when no matches are found. -- | -- | ```purescript -- | >>> indexOf (Pattern "𝐀") "b 𝐀𝐀 c 𝐀" @@ -257,7 +257,7 @@ indexOf p s = (\i -> length (CU.take i s)) <$> CU.indexOf p s -- | Returns the number of code points preceding the first match of the given -- | pattern in the string. Pattern matches preceding the given index will be --- | ignored. Returns Nothing when no matches are found. +-- | ignored. Returns `Nothing` when no matches are found. -- | -- | ```purescript -- | >>> indexOf' (Pattern "𝐀") 4 "b 𝐀𝐀 c 𝐀" @@ -272,7 +272,7 @@ indexOf' p i s = (\k -> i + length (CU.take k s')) <$> CU.indexOf p s' -- | Returns the number of code points preceding the last match of the given --- | pattern in the string. Returns Nothing when no matches are found. +-- | pattern in the string. Returns `Nothing` when no matches are found. -- | -- | ```purescript -- | >>> lastIndexOf (Pattern "𝐀") "b 𝐀𝐀 c 𝐀" @@ -292,7 +292,7 @@ lastIndexOf p s = (\i -> length (CU.take i s)) <$> CU.lastIndexOf p s -- | greater than the number of code points in the string is equivalent to -- | searching in the whole string. -- | --- | Returns Nothing when no matches are found. +-- | Returns `Nothing` when no matches are found. -- | -- | ```purescript -- | >>> lastIndexOf' (Pattern "𝐀") (-1) "b 𝐀𝐀 c 𝐀" diff --git a/src/Data/String/CodeUnits.purs b/src/Data/String/CodeUnits.purs index dfa0482..fbc1803 100644 --- a/src/Data/String/CodeUnits.purs +++ b/src/Data/String/CodeUnits.purs @@ -37,7 +37,7 @@ import Data.String.Unsafe as U ------------------------------------------------------------------------------- -- | If the string starts with the given prefix, return the portion of the --- | string left after removing it, as a Just value. Otherwise, return Nothing. +-- | string left after removing it, as a `Just` value. Otherwise, return `Nothing`. -- | -- | ```purescript -- | stripPrefix (Pattern "http:") "http://purescript.org" == Just "//purescript.org" From a8e757faa17ff24e6add938ba61a9375c867dd40 Mon Sep 17 00:00:00 2001 From: JordanMartinez Date: Tue, 15 Mar 2022 13:18:54 -0700 Subject: [PATCH 174/186] Update to v0.15.0 (#158) * Convert foreign modules to try bundling with esbuild * Replaced 'export var' with 'export const' * Removed '"use strict";' in FFI files * Update to CI to use 'unstable' purescript * Update pulp to 16.0.0-0 and psa to 0.8.2 * Update Bower dependencies to master * Update .eslintrc.json to ES6 * Fix compiler error due to Proxy type * Fix unused name compiler warnings * Added changelog entry * Update test script to use import Co-authored-by: Cyril Sobierajewicz --- .eslintrc.json | 6 +-- .github/workflows/ci.yml | 2 + CHANGELOG.md | 2 + bower.json | 38 +++++++++---------- package.json | 6 +-- src/Data/String/CodePoints.js | 15 ++++---- src/Data/String/CodeUnits.js | 32 ++++++++-------- src/Data/String/Common.js | 18 ++++----- src/Data/String/NonEmpty/Internal.purs | 3 +- src/Data/String/Regex.js | 22 +++++------ src/Data/String/Unsafe.js | 6 +-- test/Test/Data/String/NonEmpty/CodeUnits.purs | 8 ++-- 12 files changed, 76 insertions(+), 82 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 84cef4f..1c6afb9 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,11 +1,9 @@ { "parserOptions": { - "ecmaVersion": 5 + "ecmaVersion": 6, + "sourceType": "module" }, "extends": "eslint:recommended", - "env": { - "commonjs": true - }, "rules": { "strict": [2, "global"], "block-scoped-var": 2, diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 43d2897..b6ebf3a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,8 @@ jobs: - uses: actions/checkout@v2 - uses: purescript-contrib/setup-purescript@main + with: + purescript: "unstable" - uses: actions/setup-node@v1 with: diff --git a/CHANGELOG.md b/CHANGELOG.md index c3bf386..d63cbd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ Notable changes to this project are documented in this file. The format is based ## [Unreleased] Breaking changes: +- Migrate FFI to ES modules (#158 by @kl0tl and @JordanMartinez) +- Replaced polymorphic proxies with monomorphic `Proxy` (#158 by @JordanMartinez) New features: diff --git a/bower.json b/bower.json index d4c9f36..2b8dd58 100644 --- a/bower.json +++ b/bower.json @@ -16,26 +16,26 @@ "package.json" ], "dependencies": { - "purescript-arrays": "^6.0.0", - "purescript-control": "^5.0.0", - "purescript-either": "^5.0.0", - "purescript-enums": "^5.0.0", - "purescript-foldable-traversable": "^5.0.0", - "purescript-gen": "^3.0.0", - "purescript-integers": "^5.0.0", - "purescript-maybe": "^5.0.0", - "purescript-newtype": "^4.0.0", - "purescript-nonempty": "^6.0.0", - "purescript-partial": "^3.0.0", - "purescript-prelude": "^5.0.0", - "purescript-tailrec": "^5.0.0", - "purescript-tuples": "^6.0.0", - "purescript-unfoldable": "^5.0.0", - "purescript-unsafe-coerce": "^5.0.0" + "purescript-arrays": "master", + "purescript-control": "master", + "purescript-either": "master", + "purescript-enums": "master", + "purescript-foldable-traversable": "master", + "purescript-gen": "master", + "purescript-integers": "master", + "purescript-maybe": "master", + "purescript-newtype": "master", + "purescript-nonempty": "master", + "purescript-partial": "master", + "purescript-prelude": "master", + "purescript-tailrec": "master", + "purescript-tuples": "master", + "purescript-unfoldable": "master", + "purescript-unsafe-coerce": "master" }, "devDependencies": { - "purescript-assert": "^5.0.0", - "purescript-console": "^5.0.0", - "purescript-minibench": "^3.0.0" + "purescript-assert": "master", + "purescript-console": "master", + "purescript-minibench": "master" } } diff --git a/package.json b/package.json index c116cae..cffd45e 100644 --- a/package.json +++ b/package.json @@ -4,15 +4,15 @@ "clean": "rimraf output && rimraf .pulp-cache", "build": "eslint src && pulp build -- --censor-lib --strict", "test": "pulp test && npm run test:run:without_codePointAt", - "test:run:without_codePointAt": "node -e \"delete String.prototype.codePointAt; require('./output/Test.Main/index.js').main();\"", + "test:run:without_codePointAt": "node -e \"delete String.prototype.codePointAt; import('./output/Test.Main/index.js').then(m => m.main());\"", "bench:build": "purs compile 'bench/**/*.purs' 'src/**/*.purs' 'bower_components/*/src/**/*.purs'", "bench:run": "node --expose-gc -e 'require(\"./output/Bench.Main/index.js\").main()'", "bench": "npm run bench:build && npm run bench:run" }, "devDependencies": { "eslint": "^7.15.0", - "pulp": "^15.0.0", - "purescript-psa": "^0.8.0", + "pulp": "16.0.0-0", + "purescript-psa": "^0.8.2", "rimraf": "^3.0.2" } } diff --git a/src/Data/String/CodePoints.js b/src/Data/String/CodePoints.js index eead7f6..ebd9e39 100644 --- a/src/Data/String/CodePoints.js +++ b/src/Data/String/CodePoints.js @@ -1,4 +1,3 @@ -"use strict"; /* global Symbol */ var hasArrayFrom = typeof Array.from === "function"; @@ -10,13 +9,13 @@ var hasStringIterator = var hasFromCodePoint = typeof String.prototype.fromCodePoint === "function"; var hasCodePointAt = typeof String.prototype.codePointAt === "function"; -exports._unsafeCodePointAt0 = function (fallback) { +export const _unsafeCodePointAt0 = function (fallback) { return hasCodePointAt ? function (str) { return str.codePointAt(0); } : fallback; }; -exports._codePointAt = function (fallback) { +export const _codePointAt = function (fallback) { return function (Just) { return function (Nothing) { return function (unsafeCodePointAt0) { @@ -40,7 +39,7 @@ exports._codePointAt = function (fallback) { }; }; -exports._countPrefix = function (fallback) { +export const _countPrefix = function (fallback) { return function (unsafeCodePointAt0) { if (hasStringIterator) { return function (pred) { @@ -59,7 +58,7 @@ exports._countPrefix = function (fallback) { }; }; -exports._fromCodePointArray = function (singleton) { +export const _fromCodePointArray = function (singleton) { return hasFromCodePoint ? function (cps) { // Function.prototype.apply will fail for very large second parameters, @@ -74,11 +73,11 @@ exports._fromCodePointArray = function (singleton) { }; }; -exports._singleton = function (fallback) { +export const _singleton = function (fallback) { return hasFromCodePoint ? String.fromCodePoint : fallback; }; -exports._take = function (fallback) { +export const _take = function (fallback) { return function (n) { if (hasStringIterator) { return function (str) { @@ -96,7 +95,7 @@ exports._take = function (fallback) { }; }; -exports._toCodePointArray = function (fallback) { +export const _toCodePointArray = function (fallback) { return function (unsafeCodePointAt0) { if (hasArrayFrom) { return function (str) { diff --git a/src/Data/String/CodeUnits.js b/src/Data/String/CodeUnits.js index 6017fd3..47d61f9 100644 --- a/src/Data/String/CodeUnits.js +++ b/src/Data/String/CodeUnits.js @@ -1,18 +1,16 @@ -"use strict"; - -exports.fromCharArray = function (a) { +export const fromCharArray = function (a) { return a.join(""); }; -exports.toCharArray = function (s) { +export const toCharArray = function (s) { return s.split(""); }; -exports.singleton = function (c) { +export const singleton = function (c) { return c; }; -exports._charAt = function (just) { +export const _charAt = function (just) { return function (nothing) { return function (i) { return function (s) { @@ -22,7 +20,7 @@ exports._charAt = function (just) { }; }; -exports._toChar = function (just) { +export const _toChar = function (just) { return function (nothing) { return function (s) { return s.length === 1 ? just(s) : nothing; @@ -30,11 +28,11 @@ exports._toChar = function (just) { }; }; -exports.length = function (s) { +export const length = function (s) { return s.length; }; -exports.countPrefix = function (p) { +export const countPrefix = function (p) { return function (s) { var i = 0; while (i < s.length && p(s.charAt(i))) i++; @@ -42,7 +40,7 @@ exports.countPrefix = function (p) { }; }; -exports._indexOf = function (just) { +export const _indexOf = function (just) { return function (nothing) { return function (x) { return function (s) { @@ -53,7 +51,7 @@ exports._indexOf = function (just) { }; }; -exports._indexOfStartingAt = function (just) { +export const _indexOfStartingAt = function (just) { return function (nothing) { return function (x) { return function (startAt) { @@ -67,7 +65,7 @@ exports._indexOfStartingAt = function (just) { }; }; -exports._lastIndexOf = function (just) { +export const _lastIndexOf = function (just) { return function (nothing) { return function (x) { return function (s) { @@ -78,7 +76,7 @@ exports._lastIndexOf = function (just) { }; }; -exports._lastIndexOfStartingAt = function (just) { +export const _lastIndexOfStartingAt = function (just) { return function (nothing) { return function (x) { return function (startAt) { @@ -91,19 +89,19 @@ exports._lastIndexOfStartingAt = function (just) { }; }; -exports.take = function (n) { +export const take = function (n) { return function (s) { return s.substr(0, n); }; }; -exports.drop = function (n) { +export const drop = function (n) { return function (s) { return s.substring(n); }; }; -exports._slice = function (b) { +export const _slice = function (b) { return function (e) { return function (s) { return s.slice(b,e); @@ -111,7 +109,7 @@ exports._slice = function (b) { }; }; -exports.splitAt = function (i) { +export const splitAt = function (i) { return function (s) { return { before: s.substring(0, i), after: s.substring(i) }; }; diff --git a/src/Data/String/Common.js b/src/Data/String/Common.js index 111c02e..5693585 100644 --- a/src/Data/String/Common.js +++ b/src/Data/String/Common.js @@ -1,6 +1,4 @@ -"use strict"; - -exports._localeCompare = function (lt) { +export const _localeCompare = function (lt) { return function (eq) { return function (gt) { return function (s1) { @@ -13,7 +11,7 @@ exports._localeCompare = function (lt) { }; }; -exports.replace = function (s1) { +export const replace = function (s1) { return function (s2) { return function (s3) { return s3.replace(s1, s2); @@ -21,7 +19,7 @@ exports.replace = function (s1) { }; }; -exports.replaceAll = function (s1) { +export const replaceAll = function (s1) { return function (s2) { return function (s3) { return s3.replace(new RegExp(s1.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&"), "g"), s2); // eslint-disable-line no-useless-escape @@ -29,25 +27,25 @@ exports.replaceAll = function (s1) { }; }; -exports.split = function (sep) { +export const split = function (sep) { return function (s) { return s.split(sep); }; }; -exports.toLower = function (s) { +export const toLower = function (s) { return s.toLowerCase(); }; -exports.toUpper = function (s) { +export const toUpper = function (s) { return s.toUpperCase(); }; -exports.trim = function (s) { +export const trim = function (s) { return s.trim(); }; -exports.joinWith = function (s) { +export const joinWith = function (s) { return function (xs) { return xs.join(s); }; diff --git a/src/Data/String/NonEmpty/Internal.purs b/src/Data/String/NonEmpty/Internal.purs index 707b779..8722654 100644 --- a/src/Data/String/NonEmpty/Internal.purs +++ b/src/Data/String/NonEmpty/Internal.purs @@ -16,6 +16,7 @@ import Data.String as String import Data.String.Pattern (Pattern) import Data.Symbol (class IsSymbol, reflectSymbol) import Prim.TypeError as TE +import Type.Proxy (Proxy) import Unsafe.Coerce (unsafeCoerce) -- | A string that is known not to be empty. @@ -41,7 +42,7 @@ instance showNonEmptyString :: Show NonEmptyString where -- | something = nes (Proxy :: Proxy "something") -- | ``` class MakeNonEmpty (s :: Symbol) where - nes :: forall proxy. proxy s -> NonEmptyString + nes :: Proxy s -> NonEmptyString instance makeNonEmptyBad :: TE.Fail (TE.Text "Cannot create an NonEmptyString from an empty Symbol") => MakeNonEmpty "" where nes _ = NonEmptyString "" diff --git a/src/Data/String/Regex.js b/src/Data/String/Regex.js index b3be593..3196034 100644 --- a/src/Data/String/Regex.js +++ b/src/Data/String/Regex.js @@ -1,10 +1,8 @@ -"use strict"; - -exports.showRegexImpl = function (r) { +export const showRegexImpl = function (r) { return "" + r; }; -exports.regexImpl = function (left) { +export const regexImpl = function (left) { return function (right) { return function (s1) { return function (s2) { @@ -18,11 +16,11 @@ exports.regexImpl = function (left) { }; }; -exports.source = function (r) { +export const source = function (r) { return r.source; }; -exports.flagsImpl = function (r) { +export const flagsImpl = function (r) { return { multiline: r.multiline, ignoreCase: r.ignoreCase, @@ -33,7 +31,7 @@ exports.flagsImpl = function (r) { }; }; -exports.test = function (r) { +export const test = function (r) { return function (s) { var lastIndex = r.lastIndex; var result = r.test(s); @@ -42,7 +40,7 @@ exports.test = function (r) { }; }; -exports._match = function (just) { +export const _match = function (just) { return function (nothing) { return function (r) { return function (s) { @@ -60,7 +58,7 @@ exports._match = function (just) { }; }; -exports.replace = function (r) { +export const replace = function (r) { return function (s1) { return function (s2) { return s2.replace(r, s1); @@ -68,7 +66,7 @@ exports.replace = function (r) { }; }; -exports._replaceBy = function (just) { +export const _replaceBy = function (just) { return function (nothing) { return function (r) { return function (f) { @@ -87,7 +85,7 @@ exports._replaceBy = function (just) { }; }; -exports._search = function (just) { +export const _search = function (just) { return function (nothing) { return function (r) { return function (s) { @@ -98,7 +96,7 @@ exports._search = function (just) { }; }; -exports.split = function (r) { +export const split = function (r) { return function (s) { return s.split(r); }; diff --git a/src/Data/String/Unsafe.js b/src/Data/String/Unsafe.js index d7a17ca..75772aa 100644 --- a/src/Data/String/Unsafe.js +++ b/src/Data/String/Unsafe.js @@ -1,13 +1,11 @@ -"use strict"; - -exports.charAt = function (i) { +export const charAt = function (i) { return function (s) { if (i >= 0 && i < s.length) return s.charAt(i); throw new Error("Data.String.Unsafe.charAt: Invalid index."); }; }; -exports.char = function (s) { +export const char = function (s) { if (s.length === 1) return s.charAt(0); throw new Error("Data.String.Unsafe.char: Expected string of length 1."); }; diff --git a/test/Test/Data/String/NonEmpty/CodeUnits.purs b/test/Test/Data/String/NonEmpty/CodeUnits.purs index 9a33b0c..e810dd9 100644 --- a/test/Test/Data/String/NonEmpty/CodeUnits.purs +++ b/test/Test/Data/String/NonEmpty/CodeUnits.purs @@ -170,11 +170,11 @@ testNonEmptyStringCodeUnits = do log "takeWhile" assertEqual - { actual: NESCU.takeWhile (\c -> true) (nes (Proxy :: Proxy "abc")) + { actual: NESCU.takeWhile (\_ -> true) (nes (Proxy :: Proxy "abc")) , expected: Just (nes (Proxy :: Proxy "abc")) } assertEqual - { actual: NESCU.takeWhile (\c -> false) (nes (Proxy :: Proxy "abc")) + { actual: NESCU.takeWhile (\_ -> false) (nes (Proxy :: Proxy "abc")) , expected: Nothing } assertEqual @@ -192,11 +192,11 @@ testNonEmptyStringCodeUnits = do log "dropWhile" assertEqual - { actual: NESCU.dropWhile (\c -> true) (nes (Proxy :: Proxy "abc")) + { actual: NESCU.dropWhile (\_ -> true) (nes (Proxy :: Proxy "abc")) , expected: Nothing } assertEqual - { actual: NESCU.dropWhile (\c -> false) (nes (Proxy :: Proxy "abc")) + { actual: NESCU.dropWhile (\_ -> false) (nes (Proxy :: Proxy "abc")) , expected: Just (nes (Proxy :: Proxy "abc")) } assertEqual From d9ba5d0590de133d290f3a8ee12296d7bdea635b Mon Sep 17 00:00:00 2001 From: maynard Date: Fri, 25 Mar 2022 09:33:01 -0500 Subject: [PATCH 175/186] Relax Data.String.CodeUnits.slice bounds checking and drop Maybe in return type (#145) * For Data.String.CodeUnits.slice: remove bounds checking and drop `Maybe` in return type * Add changelog entry Co-authored-by: JordanMartinez --- CHANGELOG.md | 1 + src/Data/String/CodeUnits.js | 2 +- src/Data/String/CodeUnits.purs | 27 +++++++-------------------- test/Test/Data/String/CodeUnits.purs | 24 ++++++++++++++---------- 4 files changed, 23 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d63cbd2..02cb63e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ Notable changes to this project are documented in this file. The format is based Breaking changes: - Migrate FFI to ES modules (#158 by @kl0tl and @JordanMartinez) - Replaced polymorphic proxies with monomorphic `Proxy` (#158 by @JordanMartinez) +- In `slice`, drop bounds checking and `Maybe` return type (#145 by Quelklef) New features: diff --git a/src/Data/String/CodeUnits.js b/src/Data/String/CodeUnits.js index 47d61f9..2608384 100644 --- a/src/Data/String/CodeUnits.js +++ b/src/Data/String/CodeUnits.js @@ -101,7 +101,7 @@ export const drop = function (n) { }; }; -export const _slice = function (b) { +export const slice = function (b) { return function (e) { return function (s) { return s.slice(b,e); diff --git a/src/Data/String/CodeUnits.purs b/src/Data/String/CodeUnits.purs index fbc1803..5fed21f 100644 --- a/src/Data/String/CodeUnits.purs +++ b/src/Data/String/CodeUnits.purs @@ -298,30 +298,17 @@ dropWhile p s = drop (countPrefix p s) s -- | Returns the substring at indices `[begin, end)`. -- | If either index is negative, it is normalised to `length s - index`, --- | where `s` is the input string. `Nothing` is returned if either +-- | where `s` is the input string. `""` is returned if either -- | index is out of bounds or if `begin > end` after normalisation. -- | -- | ```purescript --- | slice 0 0 "purescript" == Just "" --- | slice 0 1 "purescript" == Just "p" --- | slice 3 6 "purescript" == Just "esc" --- | slice (-4) (-1) "purescript" == Just "rip" --- | slice (-4) 3 "purescript" == Nothing +-- | slice 0 0 "purescript" == "" +-- | slice 0 1 "purescript" == "p" +-- | slice 3 6 "purescript" == "esc" +-- | slice (-4) (-1) "purescript" == "rip" +-- | slice (-4) 3 "purescript" == "" -- | ``` -slice :: Int -> Int -> String -> Maybe String -slice b e s = if b' < 0 || b' >= l || - e' < 0 || e' > l || - b' > e' - then Nothing - else Just (_slice b e s) - where - l = length s - norm x | x < 0 = l + x - | otherwise = x - b' = norm b - e' = norm e - -foreign import _slice :: Int -> Int -> String -> String +foreign import slice :: Int -> Int -> String -> String -- | Splits a string into two substrings, where `before` contains the -- | characters up to (but not including) the given index, and `after` contains diff --git a/test/Test/Data/String/CodeUnits.purs b/test/Test/Data/String/CodeUnits.purs index 9cf010b..ddd512b 100644 --- a/test/Test/Data/String/CodeUnits.purs +++ b/test/Test/Data/String/CodeUnits.purs @@ -472,41 +472,45 @@ testStringCodeUnits = do log "slice" assertEqual { actual: SCU.slice 0 0 "purescript" - , expected: Just "" + , expected: "" } assertEqual { actual: SCU.slice 0 1 "purescript" - , expected: Just "p" + , expected: "p" } assertEqual { actual: SCU.slice 3 6 "purescript" - , expected: Just "esc" + , expected: "esc" } assertEqual { actual: SCU.slice 3 10 "purescript" - , expected: Just "escript" + , expected: "escript" + } + assertEqual + { actual: SCU.slice 10 10 "purescript" + , expected: "" } assertEqual { actual: SCU.slice (-4) (-1) "purescript" - , expected: Just "rip" + , expected: "rip" } assertEqual { actual: SCU.slice (-4) 3 "purescript" - , expected: Nothing -- b' > e' + , expected: "" } assertEqual { actual: SCU.slice 1000 3 "purescript" - , expected: Nothing -- b' > e' (subsumes b > l) + , expected: "" } assertEqual { actual: SCU.slice 2 (-15) "purescript" - , expected: Nothing -- e' < 0 + , expected: "" } assertEqual { actual: SCU.slice (-15) 9 "purescript" - , expected: Nothing -- b' < 0 + , expected: "purescrip" } assertEqual { actual: SCU.slice 3 1000 "purescript" - , expected: Nothing -- e > l + , expected: "escript" } From bd172f8bf87d31315a9b4462b0522db2144414c9 Mon Sep 17 00:00:00 2001 From: Mohammed Anas <6daf084a-8eaf-40fb-86c7-8500077c3b69@anonaddy.me> Date: Sun, 17 Apr 2022 13:25:57 +0000 Subject: [PATCH 176/186] Fix formatting (#160) --- src/Data/String/Regex/Flags.purs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Data/String/Regex/Flags.purs b/src/Data/String/Regex/Flags.purs index dc7a0c7..822f8d1 100644 --- a/src/Data/String/Regex/Flags.purs +++ b/src/Data/String/Regex/Flags.purs @@ -23,7 +23,7 @@ noFlags = RegexFlags { global: false , ignoreCase: false , multiline: false - , dotAll : false + , dotAll: false , sticky: false , unicode: false } @@ -34,7 +34,7 @@ global = RegexFlags { global: true , ignoreCase: false , multiline: false - , dotAll : false + , dotAll: false , sticky: false , unicode: false } @@ -45,7 +45,7 @@ ignoreCase = RegexFlags { global: false , ignoreCase: true , multiline: false - , dotAll : false + , dotAll: false , sticky: false , unicode: false } @@ -56,7 +56,7 @@ multiline = RegexFlags { global: false , ignoreCase: false , multiline: true - , dotAll : false + , dotAll: false , sticky: false , unicode: false } @@ -67,7 +67,7 @@ sticky = RegexFlags { global: false , ignoreCase: false , multiline: false - , dotAll : false + , dotAll: false , sticky: true , unicode: false } @@ -78,7 +78,7 @@ unicode = RegexFlags { global: false , ignoreCase: false , multiline: false - , dotAll : false + , dotAll: false , sticky: false , unicode: true } @@ -89,7 +89,7 @@ dotAll = RegexFlags { global: false , ignoreCase: false , multiline: false - , dotAll : true + , dotAll: true , sticky: false , unicode: false } From b2565978793ecd5ad6a460009ae8d5ea5c3b9c20 Mon Sep 17 00:00:00 2001 From: Mohammed Anas <6daf084a-8eaf-40fb-86c7-8500077c3b69@anonaddy.me> Date: Sun, 17 Apr 2022 13:26:44 +0000 Subject: [PATCH 177/186] Make `RegexFlags` a `newtype` (#159) --- CHANGELOG.md | 1 + src/Data/String/Regex/Flags.purs | 14 +++++--------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02cb63e..28863e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ Bugfixes: Other improvements: - Surround code with backticks in documentation (#148) +- Make `RegexFlags` a `newtype` and a `Newtype` instance for it(#159 by @mhmdanas) ## [v5.0.0](https://github.com/purescript/purescript-strings/releases/tag/v5.0.0) - 2021-02-26 diff --git a/src/Data/String/Regex/Flags.purs b/src/Data/String/Regex/Flags.purs index 822f8d1..6d7dd71 100644 --- a/src/Data/String/Regex/Flags.purs +++ b/src/Data/String/Regex/Flags.purs @@ -3,6 +3,7 @@ module Data.String.Regex.Flags where import Prelude import Control.MonadPlus (guard) +import Data.Newtype (class Newtype) import Data.String (joinWith) type RegexFlagsRec = @@ -15,7 +16,9 @@ type RegexFlagsRec = } -- | Flags that control matching. -data RegexFlags = RegexFlags RegexFlagsRec +newtype RegexFlags = RegexFlags RegexFlagsRec + +derive instance newtypeRegexFlags :: Newtype RegexFlags _ -- | All flags set to false. noFlags :: RegexFlags @@ -107,14 +110,7 @@ instance semigroupRegexFlags :: Semigroup RegexFlags where instance monoidRegexFlags :: Monoid RegexFlags where mempty = noFlags -instance eqRegexFlags :: Eq RegexFlags where - eq (RegexFlags x) (RegexFlags y) - = x.global == y.global - && x.ignoreCase == y.ignoreCase - && x.multiline == y.multiline - && x.dotAll == y.dotAll - && x.sticky == y.sticky - && x.unicode == y.unicode +derive newtype instance eqRegexFlags :: Eq RegexFlags instance showRegexFlags :: Show RegexFlags where show (RegexFlags flags) = From 4bc6954448d056f8aa7a659695a6ad60ad4fdf19 Mon Sep 17 00:00:00 2001 From: JordanMartinez Date: Wed, 27 Apr 2022 16:24:51 -0500 Subject: [PATCH 178/186] Prepare v6.0.0 release (1st PS 0.15.0-compatible release) (#161) * Update the bower dependencies * Update Node to 14 in CI * Update the changelog --- .github/workflows/ci.yml | 4 ++-- CHANGELOG.md | 10 ++++++++++ bower.json | 38 +++++++++++++++++++------------------- 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b6ebf3a..c69237a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,9 +16,9 @@ jobs: with: purescript: "unstable" - - uses: actions/setup-node@v1 + - uses: actions/setup-node@v2 with: - node-version: "12" + node-version: "14.x" - name: Install dependencies run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 28863e1..52a8669 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ Notable changes to this project are documented in this file. The format is based ## [Unreleased] +Breaking changes: + +New features: + +Bugfixes: + +Other improvements: + +## [v6.0.0](https://github.com/purescript/purescript-strings/releases/tag/v6.0.0) - 2022-04-27 + Breaking changes: - Migrate FFI to ES modules (#158 by @kl0tl and @JordanMartinez) - Replaced polymorphic proxies with monomorphic `Proxy` (#158 by @JordanMartinez) diff --git a/bower.json b/bower.json index 2b8dd58..cceeac4 100644 --- a/bower.json +++ b/bower.json @@ -16,26 +16,26 @@ "package.json" ], "dependencies": { - "purescript-arrays": "master", - "purescript-control": "master", - "purescript-either": "master", - "purescript-enums": "master", - "purescript-foldable-traversable": "master", - "purescript-gen": "master", - "purescript-integers": "master", - "purescript-maybe": "master", - "purescript-newtype": "master", - "purescript-nonempty": "master", - "purescript-partial": "master", - "purescript-prelude": "master", - "purescript-tailrec": "master", - "purescript-tuples": "master", - "purescript-unfoldable": "master", - "purescript-unsafe-coerce": "master" + "purescript-arrays": "^7.0.0", + "purescript-control": "^6.0.0", + "purescript-either": "^6.0.0", + "purescript-enums": "^6.0.0", + "purescript-foldable-traversable": "^6.0.0", + "purescript-gen": "^4.0.0", + "purescript-integers": "^6.0.0", + "purescript-maybe": "^6.0.0", + "purescript-newtype": "^5.0.0", + "purescript-nonempty": "^7.0.0", + "purescript-partial": "^4.0.0", + "purescript-prelude": "^6.0.0", + "purescript-tailrec": "^6.0.0", + "purescript-tuples": "^7.0.0", + "purescript-unfoldable": "^6.0.0", + "purescript-unsafe-coerce": "^6.0.0" }, "devDependencies": { - "purescript-assert": "master", - "purescript-console": "master", - "purescript-minibench": "master" + "purescript-assert": "^6.0.0", + "purescript-console": "^6.0.0", + "purescript-minibench": "^4.0.0" } } From abded947c3270cc102172b6e0302bc8f4ad86f22 Mon Sep 17 00:00:00 2001 From: JordanMartinez Date: Tue, 16 Aug 2022 17:27:30 -0500 Subject: [PATCH 179/186] Use fixed Char enums version (#163) * Use fixed Char enums version * Add changelog entry --- CHANGELOG.md | 1 + bower.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52a8669..1fdc6e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Breaking changes: New features: Bugfixes: +- Fix `Char`'s `toEnum` implementation (#163 by @JordanMartinez) Other improvements: diff --git a/bower.json b/bower.json index cceeac4..85a17c5 100644 --- a/bower.json +++ b/bower.json @@ -19,7 +19,7 @@ "purescript-arrays": "^7.0.0", "purescript-control": "^6.0.0", "purescript-either": "^6.0.0", - "purescript-enums": "^6.0.0", + "purescript-enums": "^6.0.1", "purescript-foldable-traversable": "^6.0.0", "purescript-gen": "^4.0.0", "purescript-integers": "^6.0.0", From 82c2c9a6e3bb189902357faa9396f52a5f79fa36 Mon Sep 17 00:00:00 2001 From: Jordan Martinez Date: Tue, 16 Aug 2022 17:30:01 -0500 Subject: [PATCH 180/186] Prep changelog for v6.0.1 --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fdc6e7..f36a713 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,10 +9,14 @@ Breaking changes: New features: Bugfixes: -- Fix `Char`'s `toEnum` implementation (#163 by @JordanMartinez) Other improvements: +## [v6.0.1](https://github.com/purescript/purescript-strings/releases/tag/v6.0.1) - 2022-08-16 + +Bugfixes: +- Fix `Char`'s `toEnum` implementation (#163 by @JordanMartinez) + ## [v6.0.0](https://github.com/purescript/purescript-strings/releases/tag/v6.0.0) - 2022-04-27 Breaking changes: From 3d3e2f7197d4f7aacb15e854ee9a645489555fff Mon Sep 17 00:00:00 2001 From: Jordan Martinez Date: Tue, 16 Aug 2022 17:31:08 -0500 Subject: [PATCH 181/186] v6.0.1 From 373c44a8f0ac776be836cc2d0f3b6a70a5525b07 Mon Sep 17 00:00:00 2001 From: postsolar <120750161+postsolar@users.noreply.github.com> Date: Mon, 30 Oct 2023 18:50:37 +0200 Subject: [PATCH 182/186] Better definition for `Data.String.NonEmpty.CodeUnits.fromFoldable1` (#168) --- CHANGELOG.md | 5 +++++ src/Data/String/NonEmpty/CodeUnits.purs | 6 +----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f36a713..c0def5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,11 @@ Bugfixes: Other improvements: +## [v6.0.2] + +Other improvements: +- Redefine `Data.String.NonEmpty.CodeUnits.fromFoldable1` in terms of `singleton` (#168 by @postsolar) + ## [v6.0.1](https://github.com/purescript/purescript-strings/releases/tag/v6.0.1) - 2022-08-16 Bugfixes: diff --git a/src/Data/String/NonEmpty/CodeUnits.purs b/src/Data/String/NonEmpty/CodeUnits.purs index af3de43..4e97244 100644 --- a/src/Data/String/NonEmpty/CodeUnits.purs +++ b/src/Data/String/NonEmpty/CodeUnits.purs @@ -37,7 +37,6 @@ import Data.String.NonEmpty.Internal (NonEmptyString(..), fromString) import Data.String.Pattern (Pattern) import Data.String.Unsafe as U import Partial.Unsafe (unsafePartial) -import Unsafe.Coerce (unsafeCoerce) -- For internal use only. Do not export. toNonEmptyString :: String -> NonEmptyString @@ -91,10 +90,7 @@ snoc c s = toNonEmptyString (s <> CU.singleton c) -- | Creates a `NonEmptyString` from a `Foldable1` container carrying -- | characters. fromFoldable1 :: forall f. Foldable1 f => f Char -> NonEmptyString -fromFoldable1 = F1.fold1 <<< coe - where - coe ∷ f Char -> f NonEmptyString - coe = unsafeCoerce +fromFoldable1 = F1.foldMap1 singleton -- | Converts the `NonEmptyString` into an array of characters. -- | From 72bb066aaf9f6f8ab049300bdd0ff8e90e21711d Mon Sep 17 00:00:00 2001 From: Nathan Faubion Date: Mon, 30 Oct 2023 09:52:04 -0700 Subject: [PATCH 183/186] Update CHANGELOG.md Fixup bad changelog entry. --- CHANGELOG.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0def5e..878d55d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,10 +10,6 @@ New features: Bugfixes: -Other improvements: - -## [v6.0.2] - Other improvements: - Redefine `Data.String.NonEmpty.CodeUnits.fromFoldable1` in terms of `singleton` (#168 by @postsolar) From b6654d49e2300416b25c05a00a0e6065c29fcf07 Mon Sep 17 00:00:00 2001 From: Nicholas Wolverson Date: Thu, 26 Dec 2024 12:18:56 +0000 Subject: [PATCH 184/186] Update take docstring to match the behaviour (#172) --- src/Data/String/CodePoints.purs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 65e0b55..610e497 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -314,7 +314,7 @@ lastIndexOf' p i 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 --- | points, returns the empty string. Operates in constant space and in time +-- | points, returns the entire string. Operates in constant space and in time -- | linear to the given number. -- | -- | ```purescript From 03b03a7b5df720a97f9024ae612eb94bd4155399 Mon Sep 17 00:00:00 2001 From: Eryk Ciepiela Date: Fri, 5 Sep 2025 15:21:12 +0200 Subject: [PATCH 185/186] CodeUnits length function doc references itself and is wrong (#177) * CodeUnits length function doc references itself and is wrong * reference do Data.String.CodeUnits.length * referencing Data.String.CodePoints.kength from Data.String.CodeUnits.length --- src/Data/String/CodePoints.purs | 2 +- src/Data/String/CodeUnits.purs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 610e497..b695364 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -207,7 +207,7 @@ uncons s = case CU.length s of -- | ```purescript -- | >>> length "b 𝐀𝐀 c 𝐀" -- | 8 --- | -- compare to Data.String: +-- | -- compare to Data.String.CodeUnits: -- | >>> length "b 𝐀𝐀 c 𝐀" -- | 11 -- | ``` diff --git a/src/Data/String/CodeUnits.purs b/src/Data/String/CodeUnits.purs index 5fed21f..ac78266 100644 --- a/src/Data/String/CodeUnits.purs +++ b/src/Data/String/CodeUnits.purs @@ -145,6 +145,10 @@ uncons s = Just { head: U.charAt zero s, tail: drop one s } -- | -- | ```purescript -- | length "Hello World" == 11 +-- | +-- | length "𝐀A" == 3 +-- | -- compare to Data.String.CodePoints: +-- | length "𝐀A" == 2 -- | ``` -- | foreign import length :: String -> Int From 1c9cde35c87d9b1626cf6d4b3c38c548fa45e91d Mon Sep 17 00:00:00 2001 From: triallax <6daf084a-8eaf-40fb-86c7-8500077c3b69@anonaddy.me> Date: Fri, 10 Apr 2026 22:16:25 +0000 Subject: [PATCH 186/186] Add startsWith and endsWith (#147) --- CHANGELOG.md | 1 + src/Data/String/CodePoints.purs | 2 +- src/Data/String/CodeUnits.purs | 33 +++++++++++++++++++++++--- src/Data/String/NonEmpty.purs | 2 +- src/Data/String/NonEmpty/Internal.purs | 25 +++++++++++++++++++ test/Test/Data/String/CodeUnits.purs | 14 +++++++++++ test/Test/Data/String/NonEmpty.purs | 13 ++++++++++ 7 files changed, 85 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 878d55d..f0f30c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ Notable changes to this project are documented in this file. The format is based Breaking changes: New features: +- Added `startsWith` and `endsWith` (#147) Bugfixes: diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index b695364..f5b5fd2 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -34,7 +34,7 @@ import Data.Array as Array import Data.Enum (class BoundedEnum, class Enum, Cardinality(..), defaultPred, defaultSucc, fromEnum, toEnum, toEnumWithDefaults) import Data.Int (hexadecimal, toStringAs) import Data.Maybe (Maybe(..)) -import Data.String.CodeUnits (contains, stripPrefix, stripSuffix) as Exports +import Data.String.CodeUnits (contains, stripPrefix, stripSuffix, startsWith, endsWith) as Exports import Data.String.CodeUnits as CU import Data.String.Common (toUpper) import Data.String.Pattern (Pattern) diff --git a/src/Data/String/CodeUnits.purs b/src/Data/String/CodeUnits.purs index ac78266..ec4d4cd 100644 --- a/src/Data/String/CodeUnits.purs +++ b/src/Data/String/CodeUnits.purs @@ -22,6 +22,8 @@ module Data.String.CodeUnits , dropWhile , slice , splitAt + , startsWith + , endsWith ) where import Prelude @@ -31,9 +33,10 @@ import Data.String.Pattern (Pattern(..)) import Data.String.Unsafe as U ------------------------------------------------------------------------------- --- `stripPrefix`, `stripSuffix`, and `contains` are CodeUnit/CodePoint agnostic --- as they are based on patterns rather than lengths/indices, but they need to --- be defined in here to avoid a circular module dependency +-- `stripPrefix`, `stripSuffix`, `startsWith`, `endsWith`, and `contains` are +-- CodeUnit/CodePoint agnostic as they are based on patterns rather than +-- lengths/indices, but they need to be defined in here to avoid a circular +-- module dependency ------------------------------------------------------------------------------- -- | If the string starts with the given prefix, return the portion of the @@ -61,6 +64,30 @@ stripSuffix (Pattern suffix) str = let { before, after } = splitAt (length str - length suffix) str in if after == suffix then Just before else Nothing +-- | Checks whether the given string starts with the pattern. +-- | +-- | **NOTE**: if you also want to get the string stripped of the pattern, see +-- | `stripPrefix`. +-- | +-- | ```purescript +-- | startsWith (Pattern "foo") "foobar" == true +-- | startsWith (Pattern "bar") "foobar" == false +-- | ``` +startsWith :: Pattern -> String -> Boolean +startsWith pat = isJust <<< stripPrefix pat + +-- | Checks whether the given string ends with the pattern. +-- | +-- | **NOTE**: if you also want to get the string stripped of the pattern, see +-- | `stripSuffix`. +-- | +-- | ```purescript +-- | endsWith (Pattern "bar") "foobar" == true +-- | endsWith (Pattern "foo") "foobar" == false +-- | ``` +endsWith :: Pattern -> String -> Boolean +endsWith pat = isJust <<< stripSuffix pat + -- | Checks whether the pattern appears in the given string. -- | -- | ```purescript diff --git a/src/Data/String/NonEmpty.purs b/src/Data/String/NonEmpty.purs index 6b6210c..72e10b3 100644 --- a/src/Data/String/NonEmpty.purs +++ b/src/Data/String/NonEmpty.purs @@ -4,6 +4,6 @@ module Data.String.NonEmpty , module Data.String.NonEmpty.CodePoints ) where -import Data.String.NonEmpty.Internal (NonEmptyString, class MakeNonEmpty, NonEmptyReplacement(..), appendString, contains, fromString, join1With, joinWith, joinWith1, localeCompare, nes, prependString, replace, replaceAll, stripPrefix, stripSuffix, toLower, toString, toUpper, trim, unsafeFromString) +import Data.String.NonEmpty.Internal (NonEmptyString, class MakeNonEmpty, NonEmptyReplacement(..), appendString, contains, fromString, join1With, joinWith, joinWith1, localeCompare, nes, prependString, replace, replaceAll, stripPrefix, stripSuffix, startsWith, endsWith, toLower, toString, toUpper, trim, unsafeFromString) import Data.String.Pattern (Pattern(..)) import Data.String.NonEmpty.CodePoints diff --git a/src/Data/String/NonEmpty/Internal.purs b/src/Data/String/NonEmpty/Internal.purs index 8722654..0b12623 100644 --- a/src/Data/String/NonEmpty/Internal.purs +++ b/src/Data/String/NonEmpty/Internal.purs @@ -124,6 +124,31 @@ stripPrefix pat = fromString <=< liftS (String.stripPrefix pat) stripSuffix :: Pattern -> NonEmptyString -> Maybe NonEmptyString stripSuffix pat = fromString <=< liftS (String.stripSuffix pat) + +-- | Checks whether the given string starts with the pattern. +-- | +-- | **NOTE**: if you also want to get the string stripped of the pattern, see +-- | `stripPrefix`. +-- | +-- | ```purescript +-- | startsWith (Pattern "foo") (NonEmptyString "foobar") == true +-- | startsWith (Pattern "bar") (NonEmptyString "foobar") == false +-- | ``` +startsWith :: Pattern -> NonEmptyString -> Boolean +startsWith = liftS <<< String.startsWith + +-- | Checks whether the given string ends with the pattern. +-- | +-- | **NOTE**: if you also want to get the string stripped of the pattern, see +-- | `stripSuffix`. +-- | +-- | ```purescript +-- | endsWith (Pattern "bar") (NonEmptyString "foobar") == true +-- | endsWith (Pattern "foo") (NonEmptyString "foobar") == false +-- | ``` +endsWith :: Pattern -> NonEmptyString -> Boolean +endsWith = liftS <<< String.endsWith + -- | Checks whether the pattern appears in the given string. -- | -- | ```purescript diff --git a/test/Test/Data/String/CodeUnits.purs b/test/Test/Data/String/CodeUnits.purs index ddd512b..30bf100 100644 --- a/test/Test/Data/String/CodeUnits.purs +++ b/test/Test/Data/String/CodeUnits.purs @@ -64,6 +64,20 @@ testStringCodeUnits = do , expected: Just "" } + log "startsWith" + assert $ SCU.startsWith (Pattern "foo") "foobar" + assert $ SCU.startsWith (Pattern "foo") "foo" + assert $ SCU.startsWith (Pattern "") "" + assert $ SCU.startsWith (Pattern "") "foo" + assert $ not $ SCU.startsWith (Pattern "foo") "" + + log "endsWith" + assert $ SCU.endsWith (Pattern "bar") "foobar" + assert $ SCU.endsWith (Pattern "bar") "bar" + assert $ SCU.endsWith (Pattern "") "" + assert $ SCU.endsWith (Pattern "") "bar" + assert $ not $ SCU.endsWith (Pattern "bar") "" + log "charAt" assertEqual { actual: SCU.charAt 0 "" diff --git a/test/Test/Data/String/NonEmpty.purs b/test/Test/Data/String/NonEmpty.purs index a4103ec..46f2cd0 100644 --- a/test/Test/Data/String/NonEmpty.purs +++ b/test/Test/Data/String/NonEmpty.purs @@ -144,6 +144,19 @@ testNonEmptyString = do , expected: Nothing } + log "startsWith" + assert $ NES.startsWith (Pattern "foo") (nes (Proxy :: Proxy "foobar")) + assert $ NES.startsWith (Pattern "foo") (nes (Proxy :: Proxy "foo")) + assert $ NES.startsWith (Pattern "") (nes (Proxy :: Proxy "foo")) + assert $ not $ NES.startsWith (Pattern "foo") (nes (Proxy :: Proxy "f")) + + log "endsWith" + assert $ NES.endsWith (Pattern "bar") (nes (Proxy :: Proxy "foobar")) + assert $ NES.endsWith (Pattern "bar") (nes (Proxy :: Proxy "bar")) + assert $ NES.endsWith (Pattern "") (nes (Proxy :: Proxy "f")) + assert $ NES.endsWith (Pattern "") (nes (Proxy :: Proxy "bar")) + assert $ not $ NES.endsWith (Pattern "bar") (nes (Proxy :: Proxy "b")) + log "toLower" assertEqual { actual: NES.toLower (nes (Proxy :: Proxy "bAtMaN"))