fay-base-0.21.0.0: src/Data/Text.hs
{-# OPTIONS -fno-warn-missing-methods #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE NoRebindableSyntax #-}
{-# LANGUAGE PackageImports #-}
{-# LANGUAGE StandaloneDeriving #-}
-- | Compatible API with the `text' package.
module Data.Text
( Text
-- * Creation and elimination
, pack
, unpack
, fromString
, empty
-- * Conversions
, showInt
, toShortest
-- * I/O
, putStrLn
-- * Breaking into many substrings
, splitOn
, stripSuffix
-- * Basic interface
, cons
, snoc
, append
, (<>)
, uncons
, head
, init
, last
, tail
, null
, length
-- * Special folds
, maximum
, all
, any
, concatMap
, concat
, minimum
-- * Case conversion
, toLower
, toUpper
-- * Transformations
, map
, intercalate
, intersperse
, reverse
-- * Predicates
, isPrefixOf
-- * Substrings
, drop
, take
-- * Breaking into lines and words
, unlines
, lines
) where
import Data.Data
import FFI
import Data.Nullable (fromNullable)
import Prelude (Eq,String,Int,Bool,Char,Maybe,Double,Ord,error)
import qualified "base" Data.String as B (IsString (..))
-- | A space efficient, packed, unboxed Unicode text type.
data Text
deriving instance Eq Text
deriving instance Data Text
deriving instance Typeable Text
instance Ord Text
instance B.IsString Text where fromString = error "the method fromString can never be called"
-- | O(n) The intercalate function takes a Text and a list of Texts and
-- concatenates the list after interspersing the first argument
-- between each element of the list.
intercalate :: Text -> [Text] -> Text
intercalate = ffi "%2.join(%1)"
-- | Convert from a string to text.
fromString :: String -> Text
fromString = ffi "%1"
-- | O(n) Adds a character to the end of a Text. This copies the
-- entire array in the process, unless fused. Subject to
-- fusion. Performs replacement on invalid scalar values.
snoc :: Text -> Char -> Text
snoc = ffi "%1 + %2"
-- | O(n) Adds a character to the front of a Text. This function is
-- more costly than its List counterpart because it requires copying a
-- new array. Subject to fusion. Performs replacement on invalid
-- scalar values.
cons :: Char -> Text -> Text
cons = ffi "%1 + %2"
-- | O(n) Convert a String into a Text. Subject to fusion. Performs
-- replacement on invalid scalar values.
pack :: String -> Text
pack = ffi "%1"
-- | O(n) Convert a Text into a String. Subject to fusion.
unpack :: Text -> String
unpack = ffi "%1"
-- | O(n) Appends one Text to the other by copying both of them into a
-- new Text. Subject to fusion.
append :: Text -> Text -> Text
append = ffi "%1 + %2"
-- | Append two texts.
(<>) :: Text -> Text -> Text
(<>) = ffi "%1 + %2"
-- | O(n) Returns the number of characters in a Text. Subject to
-- fusion.
length :: Text -> Int
length = ffi "%1.length"
-- | O(1) Tests whether a Text is empty or not. Subject to fusion.
null :: Text -> Bool
null = ffi "%1.length == 0"
-- | O(n) take n, applied to a Text, returns the prefix of the Text of
-- length n, or the Text itself if n is greater than the length of the
-- Text. Subject to fusion.
take :: Int -> Text -> Text
take = ffi "%2.substring(0,%1)"
-- | O(n) drop n, applied to a Text, returns the suffix of the Text
-- after the first n characters, or the empty Text if n is greater
-- than the length of the Text. Subject to fusion.
drop :: Int -> Text -> Text
drop = ffi "%2.substring(%1)"
-- | O(1) The empty Text.
-- Basic interface
empty :: Text
empty = ffi "\"\""
-- | O(n) Breaks a Text up into a list of Texts at newline Chars. The
-- resulting strings do not contain newlines.
lines :: Text -> [Text]
lines = ffi "%1.split('\\n')"
-- | O(n) Joins lines, after appending a terminating newline to each.
unlines :: [Text] -> Text
unlines = ffi "%1.join('\\n')"
-- | O(n) The isPrefixOf function takes two Texts and returns True iff
-- the first is a prefix of the second. Subject to fusion.
-- http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html
isPrefixOf :: Text -> Text -> Bool
isPrefixOf = ffi "%2.lastIndexOf(%1, 0) == 0"
-- | O(n) The intersperse function takes a character and places it
-- between the characters of a Text. Subject to fusion. Performs
-- replacement on invalid scalar values.
intersperse :: Char -> Text -> Text
intersperse = ffi "%2.split('').join(%1)"
-- | O(n) Reverse the characters of a string. Subject to fusion.
reverse :: Text -> Text
reverse = ffi "%1.split('').reverse().join('')"
-- | O(n) Return the prefix of the second string if its suffix matches
-- the entire first string.
stripSuffix :: Text -- ^ Suffix.
-> Text -- ^ Text.
-> Maybe Text
stripSuffix prefix text =
fromNullable (extract prefix text)
where extract :: Text -> Text -> Nullable Text
extract =
ffi "(function(suffix,text){ return text.substring(text.length - suffix.length) == suffix? text.substring(0,text.length - suffix.length) : null; })(%1,%2)"
-- | O(m+n) Break a Text into pieces separated by the first Text
-- argument, consuming the delimiter. An empty delimiter is
-- invalid, and will cause an error to be raised.
splitOn :: Text -> Text -> [Text]
splitOn = ffi "%2.split(%1)"
-- |
putStrLn :: Text -> Fay ()
putStrLn = ffi "console.log('%%s',%1)"
-- |
toShortest :: Double -> Text
toShortest = ffi "%1.toString()"
-- |
showInt :: Int -> Text
showInt = ffi "%1.toString()"
-- | O(1) Returns the first character and rest of a Text, or Nothing
-- if empty. Subject to fusion.
uncons :: Text -> Maybe (Char, Text)
uncons = ffi "%1[0] ? { instance: 'Just', slot1 : [%1[0],%1.slice(1)] } : { instance : 'Nothing' }"
-- | O(1) Returns the first character of a Text, which must be
-- non-empty. Subject to fusion.
head :: Text -> Char
head = ffi "%1[0] || (function () {throw new Error('Data.Text.head: empty Text'); }())"
-- | O(1) Returns the last character of a Text, which must be
-- non-empty. Subject to fusion.
last :: Text -> Char
last = ffi "%1.length ? %1[%1.length-1] : (function() { throw new Error('Data.Text.last: empty Text') })()"
-- | O(1) Returns all characters after the head of a Text, which must
-- be non-empty. Subject to fusion.
tail :: Text -> Text
tail = ffi "%1.length ? %1.slice(1) : (function () { throw new Error('Data.Text.tail: empty Text') })()"
-- | O(1) Returns all but the last character of a Text, which must be
-- non-empty. Subject to fusion.
init :: Text -> Text
init = ffi "%1.length ? %1.slice(0,-1) : (function () { throw new Error('Data.Text.init: empty Text') })()"
-- | O(n) map f t is the Text obtained by applying f to each element
-- of t. Subject to fusion. Performs replacement on invalid scalar
-- values.
map :: (Char -> Char) -> Text -> Text
map = ffi "[].map.call(%2, %1).join('')"
-- | O(n) Convert a string to lower case, using simple case
-- conversion. The result string may be longer than the input
-- string. For instance, "İ" (Latin capital letter I with dot above,
-- U+0130) maps to the sequence "i" (Latin small letter i, U+0069)
-- followed by " ̇" (combining dot above, U+0307).
toLower :: Text -> Text
toLower = ffi "%1.toLowerCase()"
-- | O(n) Convert a string to upper case, using simple case
-- conversion. The result string may be longer than the input
-- string. For instance, the German "ß" (eszett, U+00DF) maps to the
-- two-letter sequence "SS".
toUpper :: Text -> Text
toUpper = ffi "%1.toUpperCase()"
-- | O(n) Concatenate a list of Texts.
concat :: [Text] -> Text
concat = ffi "%1.join('')"
-- | O(n) Map a function over a Text that results in a Text, and
-- concatenate the results.
concatMap :: (Char -> Text) -> Text -> Text
concatMap = ffi "[].map.call(%2, %1).join('')"
-- | O(n) any p t determines whether any character in the Text t
-- satisifes the predicate p. Subject to fusion.
any :: (Char -> Bool) -> Text -> Bool
any = ffi "[].filter.call(%2, %1).length > 0"
-- | O(n) all p t determines whether all characters in the Text t
-- satisify the predicate p. Subject to fusion.
all :: (Char -> Bool) -> Text -> Bool
all = ffi "[].filter.call(%2, %1).length == %1.length"
-- | O(n) maximum returns the maximum value from a Text, which must be
-- non-empty. Subject to fusion.
maximum :: Text -> Char
maximum = ffi "(function (s) { \
\ if (s === '') { throw new Error('Data.Text.maximum: empty string'); } \
\ var max = s[0]; \
\ for (var i = 1; i < s.length; s++) { \
\ if (s[i] > max) { max = s[i]; } \
\ } \
\ return max; \
\ })(%1)"
-- | O(n) minimum returns the minimum value from a Text, which must be
-- non-empty. Subject to fusion.
minimum :: Text -> Char
minimum = ffi "(function (s) { \
\ if (s === '') { throw new Error('Data.Text.maximum: empty string'); } \
\ var min = s[0]; \
\ for (var i = 1; i < s.length; s++) { \
\ if (s[i] < min) { min = s[i]; } \
\ } \
\ return min; \
\ })(%1)"