packages feed

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 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++