Search code examples
parsinghaskellparsec

Haskell Parsec: Undo a failed many


I have some code here that works for parsing URI paths into a list of Strings. For examples /user/home would become ["user", "home"].

pathPiece :: Parser String   
pathPiece = do
      char '/'
      path <- many1 urlBaseChar
      return path

uriPath :: Parser [String]
uriPath = do
    pieces <- many pathPiece
    try $ char '/'
    return pieces

parseUriPath :: String -> [String]
parseUriPath input = case parse uriPath "(unknown)" input of
                   Left  _  -> []
                   Right xs -> xs

However, there if the path ends with another / such as /user/home/, which should be a legitimate path, the parser will fail. This is because pathPiece fails to parse the last / since there are no following urlBaseChars. I am wondering how you parse with many until it fails, and if it fails you undo character consumption.


Solution

  • Try this:

    pathPiece :: Parser String   
    pathPiece = try $ do
        char '/'
        many1 urlBaseChar
    
    uriPath :: Parser [String]
    uriPath = do
        pieces <- many pathPiece
        optional (char '/')
        return pieces
    

    You need to add a try to pathPiece. Otherwise, parsing the final / will make Parsec think that a new pathPiece has started, and without try, there's no backtracking. Also, unless you actually want to require a final /, you need to make it optional. The function try does not do that.