Yea. I had this sort of solution in mind. I just didn't think it was
the best way to do it, since it involves so much redundant code.
Anyhow, I suppose it is the best solution I can think of at the
moment. Here is the code:
type GlobError = String
globToRegex :: String -> Either GlobError String
globToRegex cs = case globToRegex' cs of
Left err -> Left err
Right result -> Right $ '^' : result ++ "$"
globToRegex' :: String -> Either GlobError String
globToRegex' "" = Right ""
globToRegex' ('*':cs) = case globToRegex' cs of
Left err -> Left err
Right result -> Right $ ".*" ++ result
globToRegex' ('?':cs) = case globToRegex' cs of
Left err -> Left err
Right result -> Right $ '.' : result
globToRegex' ('[':'!':c:cs) = case charClass cs of
Left err -> Left err
Right result -> Right $ "[^" ++ c :
result
globToRegex' ('[':c:cs) = case charClass cs of
Left err -> Left err
Right result -> Right $ '[' : c :
result
globToRegex' ('[':_) = Left "unterminated character class"
globToRegex' (c:cs) = case globToRegex' cs of
Left err -> Left err
Right result -> Right $ escape c ++ result
escape :: Char -> String
escape c | c `elem` regexChars = '\\' : [c]
| otherwise = [c]
where regexChars = "\\+()^$.{}]|"
charClass :: String -> Either GlobError String
charClass (']':cs) = case globToRegex' cs of
Left err -> Left err
Right result -> Right $ ']' : result
charClass (c:cs) = case charClass cs of
Left err -> Left err
Right result -> Right $ c : result
charClass [] = Left "unterminated character class"