RosettaCodeData/Task/Brace-expansion/Haskell/brace-expansion.hs
2023-07-01 13:44:08 -04:00

44 lines
1.3 KiB
Haskell

import qualified Text.Parsec as P
showExpansion :: String -> String
showExpansion =
(<>) . (<> "\n-->\n") <*> (either show unlines . P.parse parser [])
parser :: P.Parsec String u [String]
parser = expansion P.anyChar
expansion :: P.Parsec String u Char -> P.Parsec String u [String]
expansion =
fmap expand .
P.many .
((P.try alts P.<|> P.try alt1 P.<|> escape) P.<|>) . fmap (pure . pure)
expand :: [[String]] -> [String]
expand = foldr ((<*>) . fmap (<>)) [[]]
alts :: P.Parsec String u [String]
alts = concat <$> P.between (P.char '{') (P.char '}') (alt `sepBy2` P.char ',')
alt :: P.Parsec String u [String]
alt = expansion (P.noneOf ",}")
alt1 :: P.Parsec String u [String]
alt1 =
(\x -> ['{' : (x <> "}")]) <$>
P.between (P.char '{') (P.char '}') (P.many $ P.noneOf ",{}")
sepBy2 :: P.Parsec String u a -> P.Parsec String u b -> P.Parsec String u [a]
p `sepBy2` sep = (:) <$> p <*> P.many1 (sep >> p)
escape :: P.Parsec String u [String]
escape = pure <$> sequence [P.char '\\', P.anyChar]
main :: IO ()
main =
mapM_
(putStrLn . showExpansion)
[ "~/{Downloads,Pictures}/*.{jpg,gif,png}"
, "It{{em,alic}iz,erat}e{d,}, please."
, "{,{,gotta have{ ,\\, again\\, }}more }cowbell!"
, "{}} some }{,{\\\\{ edge, edge} \\,}{ cases, {here} \\\\\\\\\\}"
]