{-|
This module contains the curly bracket (braces) expansion implementation.
-}
module Lsql.Csv.Utils.BracketExpansion (bracketExpand) where

import Data.List
import qualified Data.Text as T

import Text.Parsec
import Text.Parsec.Prim
import Text.Parsec.Combinator
import Text.Parsec.Text
import Text.Parsec.Char

cumulatorComma :: Parser [String]
cumulatorComma :: Parser [String]
cumulatorComma = do
  [String]
atoms <- (Parser [String] -> Parser [String]
forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m a
try Parser [String]
p_range) Parser [String] -> Parser [String] -> Parser [String]
forall s u (m :: * -> *) a.
ParsecT s u m a -> ParsecT s u m a -> ParsecT s u m a
<|> Parser [String]
p_atoms
  [String] -> Parser [String]
forall a. a -> ParsecT Text () Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return [String]
atoms 
  
  where
    p_range :: Parser [String]
    p_range :: Parser [String]
p_range = do
      String
nb1 <- ParsecT Text () Identity Char -> ParsecT Text () Identity String
forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m [a]
many1 ParsecT Text () Identity Char
forall s (m :: * -> *) u. Stream s m Char => ParsecT s u m Char
digit
      ParsecT Text () Identity () -> ParsecT Text () Identity ()
forall s (m :: * -> *) t u a.
Stream s m t =>
ParsecT s u m a -> ParsecT s u m ()
optional(ParsecT Text () Identity () -> ParsecT Text () Identity ())
-> ParsecT Text () Identity () -> ParsecT Text () Identity ()
forall a b. (a -> b) -> a -> b
$ ParsecT Text () Identity ()
forall s (m :: * -> *) u. Stream s m Char => ParsecT s u m ()
spaces
      String -> ParsecT Text () Identity String
forall s (m :: * -> *) u.
Stream s m Char =>
String -> ParsecT s u m String
string String
".."
      ParsecT Text () Identity () -> ParsecT Text () Identity ()
forall s (m :: * -> *) t u a.
Stream s m t =>
ParsecT s u m a -> ParsecT s u m ()
optional(ParsecT Text () Identity () -> ParsecT Text () Identity ())
-> ParsecT Text () Identity () -> ParsecT Text () Identity ()
forall a b. (a -> b) -> a -> b
$ ParsecT Text () Identity ()
forall s (m :: * -> *) u. Stream s m Char => ParsecT s u m ()
spaces
      String
nb2 <- ParsecT Text () Identity Char -> ParsecT Text () Identity String
forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m [a]
many1 ParsecT Text () Identity Char
forall s (m :: * -> *) u. Stream s m Char => ParsecT s u m Char
digit

      [String] -> Parser [String]
forall a. a -> ParsecT Text () Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return([String] -> Parser [String]) -> [String] -> Parser [String]
forall a b. (a -> b) -> a -> b
$ (Int -> String) -> [Int] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> String
forall a. Show a => a -> String
show) ([Int] -> [String]) -> [Int] -> [String]
forall a b. (a -> b) -> a -> b
$ Int -> Int -> [Int]
get_range (String -> Int
forall a. Read a => String -> a
read String
nb1) (String -> Int
forall a. Read a => String -> a
read String
nb2)
      where
        get_range :: Int -> Int -> [Int]
        get_range :: Int -> Int -> [Int]
get_range Int
n1 Int
n2
          | Int
n1 Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
n2 = [Int] -> [Int]
forall a. [a] -> [a]
reverse([Int] -> [Int]) -> [Int] -> [Int]
forall a b. (a -> b) -> a -> b
$ Int -> Int -> [Int]
get_range Int
n2 Int
n1
          | Bool
otherwise = [Int
n1..Int
n2]

    p_atoms :: Parser [String]
p_atoms = ParsecT Text () Identity String -> Parser [String]
forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m [a]
many ParsecT Text () Identity String
p_atom

    p_atom :: Parser String
    p_atom :: ParsecT Text () Identity String
p_atom = do
      String
atom <- ParsecT Text () Identity Char -> ParsecT Text () Identity String
forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m [a]
many1(ParsecT Text () Identity Char -> ParsecT Text () Identity String)
-> ParsecT Text () Identity Char -> ParsecT Text () Identity String
forall a b. (a -> b) -> a -> b
$ String -> ParsecT Text () Identity Char
forall s (m :: * -> *) u.
Stream s m Char =>
String -> ParsecT s u m Char
noneOf String
",}"
      ParsecT Text () Identity Char -> ParsecT Text () Identity ()
forall s (m :: * -> *) t u a.
Stream s m t =>
ParsecT s u m a -> ParsecT s u m ()
optional(ParsecT Text () Identity Char -> ParsecT Text () Identity ())
-> ParsecT Text () Identity Char -> ParsecT Text () Identity ()
forall a b. (a -> b) -> a -> b
$ Char -> ParsecT Text () Identity Char
forall s (m :: * -> *) u.
Stream s m Char =>
Char -> ParsecT s u m Char
char Char
','
      String -> ParsecT Text () Identity String
forall a. a -> ParsecT Text () Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return String
atom


bracketP :: Parser [String]
bracketP :: Parser [String]
bracketP = do
  Char -> ParsecT Text () Identity Char
forall s (m :: * -> *) u.
Stream s m Char =>
Char -> ParsecT s u m Char
char Char
'{'
  [String]
ret <- Parser [String]
cumulatorComma
  Char -> ParsecT Text () Identity Char
forall s (m :: * -> *) u.
Stream s m Char =>
Char -> ParsecT s u m Char
char Char
'}'
  [String] -> Parser [String]
forall a. a -> ParsecT Text () Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return [String]
ret

afterBracketP :: Parser String
afterBracketP :: ParsecT Text () Identity String
afterBracketP = do
  Char -> ParsecT Text () Identity Char
forall s (m :: * -> *) u.
Stream s m Char =>
Char -> ParsecT s u m Char
char Char
'{'
  ParsecT Text () Identity Char -> ParsecT Text () Identity String
forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m [a]
many(ParsecT Text () Identity Char -> ParsecT Text () Identity String)
-> ParsecT Text () Identity Char -> ParsecT Text () Identity String
forall a b. (a -> b) -> a -> b
$ String -> ParsecT Text () Identity Char
forall s (m :: * -> *) u.
Stream s m Char =>
String -> ParsecT s u m Char
noneOf String
"}"
  Char -> ParsecT Text () Identity Char
forall s (m :: * -> *) u.
Stream s m Char =>
Char -> ParsecT s u m Char
char Char
'}'
  String
ret <- ParsecT Text () Identity Char -> ParsecT Text () Identity String
forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m [a]
many(ParsecT Text () Identity Char -> ParsecT Text () Identity String)
-> ParsecT Text () Identity Char -> ParsecT Text () Identity String
forall a b. (a -> b) -> a -> b
$ String -> ParsecT Text () Identity Char
forall s (m :: * -> *) u.
Stream s m Char =>
String -> ParsecT s u m Char
noneOf String
""
  String -> ParsecT Text () Identity String
forall a. a -> ParsecT Text () Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return String
ret


-- | The curly brackets (braces) expand function
--
-- The argument is a `String`, which you want to expand. Returns a list of expanded `String`s.
--
-- There are given a few usage examples:
--
-- >>> bracketExpand "car{A,B}"
-- ["carA","carB"]
--
-- >>> bracketExpand "car{1..5}"
-- ["car1","car2","car3","car4","car5"]
--
-- >>> bracketExpand "{car,bus}{0..2}"
-- ["car0","car1","car2","bus0","bus1","bus2"]

bracketExpand :: String -> [String]
bracketExpand :: String -> [String]
bracketExpand String
input =
  let first_expand :: [String]
first_expand = String -> [String]
parseOne String
input in
  [[String]] -> [String]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat([[String]] -> [String]) -> [[String]] -> [String]
forall a b. (a -> b) -> a -> b
$ (String -> [String]) -> [String] -> [[String]]
forall a b. (a -> b) -> [a] -> [b]
map (String -> [String]
recOne) [String]
first_expand

  where
    parseOne :: String -> [String]
    parseOne :: String -> [String]
parseOne String
inp = case Parser [String] -> String -> Text -> Either ParseError [String]
forall s t a.
Stream s Identity t =>
Parsec s () a -> String -> s -> Either ParseError a
parse Parser [String]
bracketP String
"bracket expansion"(Text -> Either ParseError [String])
-> Text -> Either ParseError [String]
forall a b. (a -> b) -> a -> b
$ String -> Text
T.pack String
inp of
      Left ParseError
_ -> [String
inp]
      Right [String]
ret -> (String -> String) -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (String -> String -> String
forall a. [a] -> [a] -> [a]
++String
after_bracket) [String]
ret

      where
        after_bracket :: String
after_bracket = case ParsecT Text () Identity String
-> String -> Text -> Either ParseError String
forall s t a.
Stream s Identity t =>
Parsec s () a -> String -> s -> Either ParseError a
parse ParsecT Text () Identity String
afterBracketP String
"bracket expansion"(Text -> Either ParseError String)
-> Text -> Either ParseError String
forall a b. (a -> b) -> a -> b
$ String -> Text
T.pack String
inp of
          Left ParseError
err -> String -> String
forall a. HasCallStack => String -> a
error(String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ ParseError -> String
forall a. Show a => a -> String
show ParseError
err
          Right String
ret -> String
ret

    recOne :: String -> [String]
    recOne :: String -> [String]
recOne [Char
x] = [[Char
x]]
    recOne (Char
i:String
inp) = (String -> String) -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (\String
x -> Char
iChar -> String -> String
forall a. a -> [a] -> [a]
:String
x) (String -> [String]
bracketExpand String
inp)