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..c69237a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,10 +13,12 @@ jobs: - uses: actions/checkout@v2 - uses: purescript-contrib/setup-purescript@main + 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 311b72b..f0f30c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,32 @@ Notable changes to this project are documented in this file. The format is based Breaking changes: New features: +- Added `startsWith` and `endsWith` (#147) Bugfixes: 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: +- 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: +- 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: + +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/bower.json b/bower.json index d4c9f36..85a17c5 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": "^7.0.0", + "purescript-control": "^6.0.0", + "purescript-either": "^6.0.0", + "purescript-enums": "^6.0.1", + "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": "^5.0.0", - "purescript-console": "^5.0.0", - "purescript-minibench": "^3.0.0" + "purescript-assert": "^6.0.0", + "purescript-console": "^6.0.0", + "purescript-minibench": "^4.0.0" } } 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/CodePoints.purs b/src/Data/String/CodePoints.purs index 7d0f528..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) @@ -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 @@ -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 -- | ``` @@ -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 𝐀" @@ -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 diff --git a/src/Data/String/CodeUnits.js b/src/Data/String/CodeUnits.js index 6017fd3..2608384 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/CodeUnits.purs b/src/Data/String/CodeUnits.purs index dfa0482..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,13 +33,14 @@ 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 --- | 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" @@ -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 @@ -145,6 +172,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 @@ -298,30 +329,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/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.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/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. -- | diff --git a/src/Data/String/NonEmpty/Internal.purs b/src/Data/String/NonEmpty/Internal.purs index 707b779..0b12623 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 "" @@ -123,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/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/Regex/Flags.purs b/src/Data/String/Regex/Flags.purs index dc7a0c7..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 @@ -23,7 +26,7 @@ noFlags = RegexFlags { global: false , ignoreCase: false , multiline: false - , dotAll : false + , dotAll: false , sticky: false , unicode: false } @@ -34,7 +37,7 @@ global = RegexFlags { global: true , ignoreCase: false , multiline: false - , dotAll : false + , dotAll: false , sticky: false , unicode: false } @@ -45,7 +48,7 @@ ignoreCase = RegexFlags { global: false , ignoreCase: true , multiline: false - , dotAll : false + , dotAll: false , sticky: false , unicode: false } @@ -56,7 +59,7 @@ multiline = RegexFlags { global: false , ignoreCase: false , multiline: true - , dotAll : false + , dotAll: false , sticky: false , unicode: false } @@ -67,7 +70,7 @@ sticky = RegexFlags { global: false , ignoreCase: false , multiline: false - , dotAll : false + , dotAll: false , sticky: true , unicode: false } @@ -78,7 +81,7 @@ unicode = RegexFlags { global: false , ignoreCase: false , multiline: false - , dotAll : false + , dotAll: false , sticky: false , unicode: true } @@ -89,7 +92,7 @@ dotAll = RegexFlags { global: false , ignoreCase: false , multiline: false - , dotAll : true + , dotAll: true , sticky: false , unicode: false } @@ -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) = 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/CodeUnits.purs b/test/Test/Data/String/CodeUnits.purs index 9cf010b..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 "" @@ -472,41 +486,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" } 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")) 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