Why write a glob engine at all when you already have a fast regex implementation that can match both exact paths and plausible subtrees?<p>The bulk of the haskell code to do this:<p><pre><code> parseGlob :: Char -> Char -> String -> Parser Glob
parseGlob escC sepC forbid =
many1' (gpart <|> sep <|> glob <|> alt) >>= return . GGroup . V.fromList
where gpart = globPart escC (sepC : (forbid ++ "{*")) >>= return . GPart
sep = satisfy (== ch2word sepC) >> return GSeparator
alt = do
_ <- AttoC.char '{'
choices <- sepBy' (GEmpty `option` parseGlob escC sepC (",}" ++ forbid)) (char ',')
_ <- AttoC.char '}'
return $ GAlternate $ V.fromList choices
glob = do
res <- takeWhile1 (== ch2word '*')
if B.length res == 1 then
return GSingle
else
return GDouble
wrapParens s = T.concat ["(", s, ")"]
globRegex :: Char -> Glob -> T.Text
globRegex sep GSingle = T.concat ["([^", T.singleton sep, "]*|\\", T.singleton sep, ")"]
globRegex _ GDouble = ".*"
globRegex _ GEmpty = ""
globRegex sep GSeparator = T.singleton sep
globRegex sep (GRepeat a) = T.concat ["(", T.concat (V.toList $ fmap (globRegex sep) a), ")*"]
globRegex sep (GGroup a) = T.concat $ V.toList $ fmap (globRegex sep) a
globRegex _ (GPart p) = T.concatMap efun base
where base = TE.decodeUtf8 p
escChars = S.fromList ".[]()\\{}^$*+"
efun c = if S.member c escChars
then T.concat ["\\", T.singleton c]
else T.singleton c
globRegex sep (GAlternate a) =
if V.null alts
then ""
else T.concat [altsStr, if hasEmpty then "?" else ""]
where hasEmpty = isJust $ V.find (== GEmpty) a
alts = fmap (globRegex sep) $ V.filter (/= GEmpty) a
altsStr = wrapParens $ T.intercalate "|" $ V.toList alts</code></pre>