curly-expander-0.2.0.2: Text/CurlyExpander.hs
{-|
Module : Text.CurlyExpander
License : LGPL-3
Maintainer : p-w@stty.cz
Stability : testing
Portability : POSIX
This is the main module of the curly-expander package.
-}
{-# LANGUAGE OverloadedStrings #-}
module Text.CurlyExpander (curlyExpand) where
import qualified Data.Text as T
import qualified Data.Text.Lazy as L
import Data.Text.Lazy.Builder.Int (decimal)
import Data.Text.Lazy.Builder (toLazyText)
import Text.Parsec
import Text.Parsec.Text
import Data.Char
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
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]
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]
where
n1 = ord c1
n2 = ord c2
p_atoms :: Parser [L.Text]
p_atoms = do
molecule <- many1$ try p_atom
terminal_atom <- innerInputP
return $ (concat molecule) ++ terminal_atom
p_atom :: Parser [L.Text]
p_atom = do
atom <- innerInputP
_ <- char ','
return atom
bracketP :: Parser [L.Text]
bracketP = do
_ <- char '{'
ret <- cumulatorComma
_ <- char '}'
return$ ret
charP :: Parser [L.Text]
charP = do
c <- anyChar
return [L.pack [c]]
nonSpecialCharP :: Parser [L.Text]
nonSpecialCharP = do
c <- noneOf ",}"
return [L.pack [c]]
innerNonEmptyInputP :: Parser [L.Text]
innerNonEmptyInputP = do
molecule <- (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 <- (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.
--
-- There are given few usage examples:
--
-- >>> curlyExpand "car{A,B}"
-- ["carA","carB"]
--
-- >>> curlyExpand "car{1..5}"
-- ["car1","car2","car3","car4","car5"]
--
-- >>> curlyExpand "car{{A,B},{C,D}}"
-- ["carA", "carB", "carC", "carD"]
--
-- >>> curlyExpand "{car,bus}{A..C}"
-- ["carA", "carB", "carC", "busA", "busB", "busC"]
--
-- Be aware, that these examples will run only with `OverloadedStrings` language extension and proper `Data.Text` imports.
curlyExpand :: T.Text -> [T.Text]
curlyExpand input =
case parse inputP "bracket expansion"$ input of
Left _ -> [input]
Right ret -> map L.toStrict ret