parsec: string choice parser with useful error messages

后端 未结 3 1358
野趣味
野趣味 2021-01-14 09:48

Let\'s have following parser:

parser :: GenParser Char st String
parser = choice (fmap (try . string) [\"head\", \"tail\", \"tales\"]
                    <         


        
3条回答
  •  深忆病人
    2021-01-14 10:06

    Here's what I've got with Parsec:

    λ> let parser = choice $ fmap (try . string) ["head", "tail", "tales"]
    λ> parseTest parser "ta"
    parse error at (line 1, column 1):
    unexpected "t"
    expecting "head", "tail" or "tales"
    

    If you care to try modern version of Parsec — Megaparsec, you will end up with:

    λ> let parser = choice $ fmap (try . string) ["head", "tail", "tales"]
    λ> parseTest parser "ta"
    1:1:
    unexpected "ta" or 't'
    expecting "head", "tail", or "tales"
    

    What's going on here? First, when we parse ordered collection of characters, like with string, we display incorrect input completely. This is much better in our opinion because:

    λ> parseTest (string "when" <* eof) "well"
    1:1:
    unexpected "we"
    expecting "when"
    

    We're pointing to beginning of the word and we show the whole thing that is not correct (up to first mismatching character) and the whole thing we expect. This is more readable in my opinion. Only parsers built on tokens work this way (that is, when we're trying to match fixed string, case-insensitive variant is available).

    Then, what about unexpected "ta" or 't', why do we get 't' part? This is also absolutely correct, because with your collection of alternatives, the first letter 't' can be also unexpected by itself because you have an alternative that doesn't start with 't'. Let's see another example:

    λ> let parser = choice $ fmap (try . string) ["tall", "tail", "tales"]
    λ> parseTest parser "ta"
    1:1:
    unexpected "ta"
    expecting "tail", "tales", or "tall"
    

    Or how about:

    λ> parseTest (try (string "lexer") <|> string "lexical") "lex"
    1:1:
    unexpected "lex"
    expecting "lexer" or "lexical"
    

    Parsec:

    λ> parseTest (try (string "lexer") <|> string "lexical") "lex"
    parse error at (line 1, column 1):
    unexpected end of input
    expecting "lexical"
    

    Why take pains to make it work when it can “just work”?

    There are many other great things about Megaparsec, if you're interested, you can learn more about it here. It's hard to compete with Parsec, but we have written our own tutorials and our docs are very good.

提交回复
热议问题