MCH
MCH

Reputation: 2214

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.

Upvotes: 3

Views: 274

Answers (2)

kosmikus
kosmikus

Reputation: 19657

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.

Upvotes: 5

Toxaris
Toxaris

Reputation: 7276

I think you can use many1 urlBaseChar `sepEndBy` char '/' here. See sepEndBy in Parsec.

Upvotes: 1

Related Questions