curly-expander 0.2.0.3 → 0.3.0.0
raw patch · 5 files changed
+339/−80 lines, 5 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Text.CurlyExpander: ExpandConfig :: BackslashConfig -> [(String, String)] -> Bool -> Bool -> ExpandConfig
+ Text.CurlyExpander: NoHandle :: BackslashConfig
+ Text.CurlyExpander: Preserve :: BackslashConfig
+ Text.CurlyExpander: Standard :: BackslashConfig
+ Text.CurlyExpander: [allowOneElementExpand] :: ExpandConfig -> Bool
+ Text.CurlyExpander: [backslashConfig] :: ExpandConfig -> BackslashConfig
+ Text.CurlyExpander: [persistQuotePairs] :: ExpandConfig -> Bool
+ Text.CurlyExpander: [quotePairs] :: ExpandConfig -> [(String, String)]
+ Text.CurlyExpander: customCurlyExpand :: ExpandConfig -> Text -> [Text]
+ Text.CurlyExpander: data BackslashConfig
+ Text.CurlyExpander: data ExpandConfig
+ Text.CurlyExpander: defaultExpandConfig :: ExpandConfig
+ Text.CurlyExpander: instance GHC.Classes.Eq Text.CurlyExpander.BackslashConfig
Files
- CHANGELOG.md +3/−0
- README.md +1/−1
- Text/CurlyExpander.hs +204/−76
- curly-expander.cabal +8/−3
- tests/CustomCaseTest.hs +123/−0
CHANGELOG.md view
@@ -1,5 +1,8 @@ # Revision history for curly-expander +## 0.3.0.0+* Added customCurlyExpand function.+ ## 0.2.0.3 * Improved README
README.md view
@@ -1,4 +1,4 @@ # curly-expander-This is tool for curly brackets expanding, similar to bash curly expanding.+This is tool for curly brackets expanding, similar to bash curly expanding. It also contain an extended version of it. Please refer to the package description on [Hackage](https://hackage.haskell.org/package/curly-expander-0.2.0.2/docs/Text-CurlyExpander.html) for more information.
Text/CurlyExpander.hs view
@@ -5,13 +5,21 @@ Stability : testing Portability : POSIX -This is the main module of the curly-expander package.+This is the main (and only) module of the curly-expander package. -} {-# LANGUAGE OverloadedStrings #-} -module Text.CurlyExpander (curlyExpand) where+module Text.CurlyExpander + (+ curlyExpand, + BackslashConfig (NoHandle, Preserve, Standard), + ExpandConfig (ExpandConfig, quotePairs, backslashConfig, persistQuotePairs, allowOneElementExpand), + defaultExpandConfig, + customCurlyExpand+ ) +where import qualified Data.Text as T import qualified Data.Text.Lazy as L@@ -24,101 +32,223 @@ import Data.Char +-- | This configuration specify, how should be backslashes handled.+-- It is part of `ExpandConfig`.+data BackslashConfig = + -- | If no handle is used, then backslashes are not handled in any special way.+ NoHandle | -cumulatorComma :: Parser [L.Text]-cumulatorComma = do- atoms <- (try p_range) <|> (try p_char_range) <|> p_atoms- return atoms - - where- p_range :: Parser [L.Text]- p_range = do- nb1 <- many1 digit- _ <- string ".."- nb2 <- many1 digit+ -- | If preserve is used, backslashes are processed, any backslashed char is processed as nonspecial char + -- and backslashes aren't deleted from result.+ Preserve | - return$ map (toLazyText . decimal) $ get_range (read nb1) (read nb2)- where- get_range :: Int -> Int -> [Int]- get_range n1 n2- | n1 > n2 = reverse$ get_range n2 n1- | otherwise = [n1..n2]+ -- | If standard is used, backslashes are processed, any backslashed char is processed as nonspecial char+ -- and backslashes are deleted from result.+ Standard+ deriving Eq - p_char_range :: Parser [L.Text]- p_char_range = do- char1 <- anyChar- _ <- string ".."- char2 <- anyChar++-- | The curly braces expand config. +-- It is used in `customCurlyExpand`.+data ExpandConfig = ExpandConfig {+ -- | The configuration, which defines, how should be backslashes handled (\\)+ backslashConfig :: BackslashConfig,++ -- | Quote pairs, which encloses a substrings, tells expander, that the substring shouldn't be expanded.+ -- For example (\"[\", \"]\") pairs tells to expander, that anything inside [ANYTHING] shouldn't be expanded.+ quotePairs :: [(String, String)],++ -- | If true, quote pairs aren't deleted. Otherwise they are deleted from a result.+ persistQuotePairs :: Bool,++ -- | If true, curly brackets around one element will be deleted. Otherwise they are persisted.+ allowOneElementExpand :: Bool+ }+++-- | The default curly braces expand function config.+-- By default backslashes are not handeled, there are no quote pairs and one element expand is forbidden.+-- See the source code for details.+defaultExpandConfig :: ExpandConfig+defaultExpandConfig = ExpandConfig { + backslashConfig = NoHandle, + quotePairs = [],+ persistQuotePairs = False,+ allowOneElementExpand = False+ }++-- | Custom curly braces (brackets) expand function.+-- It works in the same way as curlyExpand, bud accept custom configuration `ExpandConfig` in the first argument.++customCurlyExpand :: ExpandConfig -> T.Text -> [T.Text]+customCurlyExpand config input =+ case parse inputP "bracket expansion"$ input of+ Left _ -> [input]+ Right ret -> map L.toStrict ret++ where + cumulatorComma :: Parser [L.Text]+ cumulatorComma = do+ atoms <- (try p_range) <|> (try p_char_range) <|> p_atoms+ return atoms - return [ L.pack [p] | p <- get_range char1 char2 ] where- get_range :: Char -> Char -> [Char]- get_range c1 c2- | n1 > n2 = reverse$ get_range c2 c1- | otherwise = map chr [n1..n2]+ p_range :: Parser [L.Text]+ p_range = do+ nb1 <- many1 digit+ _ <- string ".."+ nb2 <- many1 digit + return$ map (toLazyText . decimal) $ get_range (read nb1) (read nb2) where- n1 = ord c1- n2 = ord c2+ get_range :: Int -> Int -> [Int]+ get_range n1 n2+ | n1 > n2 = reverse$ get_range n2 n1+ | otherwise = [n1..n2] - p_atoms :: Parser [L.Text]- p_atoms = do- molecule <- many1$ try p_atom- terminal_atom <- innerInputP- return $ (concat molecule) ++ terminal_atom+ p_char_range :: Parser [L.Text]+ p_char_range = do+ char1 <- anyChar+ _ <- string ".."+ char2 <- anyChar+ + return [ L.pack [p] | p <- get_range char1 char2 ]+ where+ get_range :: Char -> Char -> [Char]+ get_range c1 c2+ | n1 > n2 = reverse$ get_range c2 c1+ | otherwise = map chr [n1..n2] - p_atom :: Parser [L.Text]- p_atom = do+ where+ n1 = ord c1+ n2 = ord c2 - atom <- innerInputP- _ <- char ','- return atom+ p_atoms :: Parser [L.Text]+ p_atoms = do+ molecule <- moleculeP+ terminal_atom <- innerInputP+ return $ (concat molecule) ++ terminal_atom + where + moleculeP :: Parser [[L.Text]]+ moleculeP = + if allowOneElementExpand config; then+ many (try p_atom)+ else+ many1 (try p_atom) -bracketP :: Parser [L.Text]-bracketP = do+ p_atom :: Parser [L.Text]+ p_atom = do - _ <- char '{'- ret <- cumulatorComma- _ <- char '}'+ atom <- innerInputP+ _ <- char ','+ return atom - return$ ret -charP :: Parser [L.Text]-charP = do- c <- anyChar- return [L.pack [c]]+ bracketP :: Parser [L.Text]+ bracketP = do -nonSpecialCharP :: Parser [L.Text]-nonSpecialCharP = do- c <- noneOf ",}"- return [L.pack [c]]+ _ <- char '{'+ ret <- cumulatorComma+ _ <- char '}' -innerNonEmptyInputP :: Parser [L.Text]-innerNonEmptyInputP = do- molecule <- (try bracketP <|> nonSpecialCharP)- rest <- innerInputP+ return$ ret - return [ L.append a b | a <- molecule, b <- rest ]+ charP :: Parser [L.Text]+ charP = do+ c <- anyChar+ return [L.pack [c]] -innerInputP :: Parser [L.Text]-innerInputP = (innerNonEmptyInputP <|> emptyInputP)+ nonSpecialCharP :: Parser [L.Text]+ nonSpecialCharP = do+ c <- noneOf ",}"+ return [L.pack [c]] -nonEmptyInputP :: Parser [L.Text]-nonEmptyInputP = do- molecule <- (try bracketP <|> charP)- rest <- inputP+ backslashedP :: Parser [L.Text]+ backslashedP = do+ if handleBackslash then do+ _ <- char '\\'+ c <- anyChar - return [ L.append a b | a <- molecule, b <- rest ]+ return$ getReturnValue c+ else do+ unexpected "Char is not backslashed." -emptyInputP :: Parser [L.Text]-emptyInputP = do- return [""]+ where+ handleBackslash :: Bool+ handleBackslash = + if backslashConfig config == NoHandle then+ False+ else + True -inputP :: Parser [L.Text]-inputP = (nonEmptyInputP <|> emptyInputP)- + getReturnValue :: Char -> [L.Text]+ getReturnValue c =+ if backslashConfig config == Preserve then+ [ L.pack ['\\', c] ]+ else+ [ L.pack [c] ] + specialQuotedP :: (String, String) -> Parser [L.Text]+ specialQuotedP (lQuote,rQuote) = do+ _ <- string lQuote+ ret <- quoteNext++ return$ [enrichReturnValue ret]+ where+ quoteClosure :: Parser L.Text+ quoteClosure = do+ _ <- string rQuote+ return ""++ quoteNextChar :: Parser L.Text+ quoteNextChar = do+ c <- anyChar+ rest <- quoteNext+ return$ L.pack [c] `L.append` rest++ quoteNext :: Parser L.Text+ quoteNext = (try quoteClosure <|> quoteNextChar)++ enrichReturnValue :: L.Text -> L.Text+ enrichReturnValue ret = + if persistQuotePairs config; then+ (L.pack lQuote) `L.append` ret `L.append` (L.pack rQuote) + else+ ret++ + quotedP :: [(String, String)] -> Parser [L.Text]+ quotedP (quotes : rest) = (try$ specialQuotedP quotes) <|> quotedP rest+ quotedP [] = unexpected "String is not quoted."++ allQuotedP :: Parser [L.Text]+ allQuotedP = quotedP$ quotePairs config++ innerNonEmptyInputP :: Parser [L.Text]+ innerNonEmptyInputP = do+ molecule <- (backslashedP <|> try allQuotedP <|> try bracketP <|> nonSpecialCharP)+ rest <- innerInputP++ return [ L.append a b | a <- molecule, b <- rest ]++ innerInputP :: Parser [L.Text]+ innerInputP = (innerNonEmptyInputP <|> emptyInputP)++ nonEmptyInputP :: Parser [L.Text]+ nonEmptyInputP = do+ molecule <- (backslashedP <|> try allQuotedP <|> try bracketP <|> charP)+ rest <- inputP++ return [ L.append a b | a <- molecule, b <- rest ]++ emptyInputP :: Parser [L.Text]+ emptyInputP = do+ return [""]++ inputP :: Parser [L.Text]+ inputP = (nonEmptyInputP <|> emptyInputP)+ -- | Curly braces (brackets) expand function -- -- First argument is a `Data.Text`, which you want to expand. Second argument is a list of expanded `Data.Text`s.@@ -141,7 +271,5 @@ curlyExpand :: T.Text -> [T.Text] curlyExpand input =- case parse inputP "bracket expansion"$ input of- Left _ -> [input]- Right ret -> map L.toStrict ret+ customCurlyExpand defaultExpandConfig input
curly-expander.cabal view
@@ -1,9 +1,9 @@ cabal-version: 3.0 name: curly-expander-version: 0.2.0.3+version: 0.3.0.0 synopsis: Curly braces (brackets) expanding-description: A library for curly braces (brackets) expanding - similar to bash curly expanding+description: A library for curly braces (brackets) expanding - similar to bash curly expanding. It also contain an extended version of it. license: LGPL-3.0-only license-file: LICENSE@@ -47,6 +47,11 @@ main-is: NestedCaseTest.hs type: exitcode-stdio-1.0 +test-suite custom-case-test+ import: tests+ main-is: CustomCaseTest.hs+ type: exitcode-stdio-1.0+ source-repository head type: git location: https://github.com/stastnypremysl/curly-expander@@ -54,4 +59,4 @@ source-repository this type: git location: https://github.com/stastnypremysl/curly-expander- tag: 0.2.0.3+ tag: 0.3.0.0
+ tests/CustomCaseTest.hs view
@@ -0,0 +1,123 @@+{-# LANGUAGE OverloadedStrings #-}+module Main where++import Prelude+import Control.Monad+import System.Exit +import Text.CurlyExpander++import qualified Data.Text as T++denyOneElement :: Bool+denyOneElement = (curlyExpand "{a}" == ["{a}"])++allowOneElement :: Bool+allowOneElement = (expand "{a}" == ["a"])+ where+ expand :: T.Text -> [T.Text]+ expand = customCurlyExpand (defaultExpandConfig {allowOneElementExpand = True})++escapedString :: T.Text+escapedString = "\\ab\\c"++escapedString2 :: T.Text+escapedString2 = "a{1,2}bc\\{,5}"++noEscaping :: Bool+noEscaping = (curlyExpand escapedString == [escapedString])++preserveEscaping :: Bool+preserveEscaping = (expand escapedString == [escapedString])+ where+ expand :: T.Text -> [T.Text]+ expand = customCurlyExpand (defaultExpandConfig {backslashConfig = Preserve})++standardEscapeConfig :: ExpandConfig+standardEscapeConfig = defaultExpandConfig {backslashConfig = Standard}++standardEscaping :: Bool+standardEscaping = (customCurlyExpand standardEscapeConfig escapedString == ["abc"])++standardEscaping2 :: Bool+standardEscaping2 = (customCurlyExpand standardEscapeConfig escapedString2 == ["a1bc{,5}", "a2bc{,5}"])++quotedString1 :: T.Text+quotedString1 = "abc''abc''abc"++quotedString2 :: T.Text+quotedString2 = "a[ab]cc"++quotedString3 :: T.Text+quotedString3 = "a[{a,b}]c"++quotedString4 :: T.Text+quotedString4 = "a{b,[{1..2}]}c"++qPairs1 :: [(String, String)]+qPairs1 = [("[", "]")]++qPairs2 :: [(String, String)]+qPairs2 = qPairs1 ++ [("''", "''")]++queted1PersistE :: T.Text -> [T.Text]+queted1PersistE = customCurlyExpand (defaultExpandConfig {persistQuotePairs = True, quotePairs = qPairs1})++queted1NopersistE :: T.Text -> [T.Text]+queted1NopersistE = customCurlyExpand (defaultExpandConfig {persistQuotePairs = False, quotePairs = qPairs1})++queted2PersistE :: T.Text -> [T.Text]+queted2PersistE = customCurlyExpand (defaultExpandConfig {persistQuotePairs = True, quotePairs = qPairs2})++queted2NopersistE :: T.Text -> [T.Text]+queted2NopersistE = customCurlyExpand (defaultExpandConfig {persistQuotePairs = False, quotePairs = qPairs2})++queted1Persist1 :: Bool+queted1Persist1 = ( queted1PersistE quotedString1 == [quotedString1] )++queted1Nopersist1 :: Bool+queted1Nopersist1 = ( queted1NopersistE quotedString1 == [quotedString1] )++queted2Persist1 :: Bool+queted2Persist1 = ( queted2PersistE quotedString1 == [quotedString1] )++queted2Nopersist1 :: Bool+queted2Nopersist1 = ( queted2NopersistE quotedString1 == ["abcabcabc"] )++queted2Persist2 :: Bool+queted2Persist2 = ( queted2PersistE quotedString2 == [quotedString2] )++queted2Nopersist2 :: Bool+queted2Nopersist2 = ( queted2NopersistE quotedString2 == ["aabcc"] )++queted2Persist3 :: Bool+queted2Persist3 = ( queted2PersistE quotedString3 == [quotedString3] )++queted2Nopersist4 :: Bool+queted2Nopersist4 = ( queted2NopersistE quotedString4 == ["abc", "a{1..2}c"] )++main :: IO a+main = do+ when (not denyOneElement) $ die "Test denyOneElement failed"+ when (not allowOneElement) $ die "Test allowOneElement failed"++ when (not noEscaping) $ die "Test noEscaping failed"+ when (not preserveEscaping) $ die "Test preserveEscaping failed"+ when (not standardEscaping) $ die "Test standardEscaping failed"+ when (not standardEscaping2) $ die "Test standardEscaping2 failed"++ when (not queted1Persist1) $ die "Test queted1Persist1 failed"+ when (not queted1Nopersist1) $ die "Test queted1Nopersist1 failed"+ when (not queted2Persist1) $ die "Test queted2Persist1 failed"+ when (not queted2Nopersist1) $ die "Test queted2Nopersist1 failed"++ when (not queted2Persist2) $ die "Test queted2Persist2 failed"+ when (not queted2Nopersist2) $ die "Test queted2Nopersist2 failed"++ when (not queted2Persist3) $ die "Test queted2Persist3 failed"++ when (not queted2Nopersist4) $ die "Test queted2Nopersist4 failed"+++ exitSuccess++