Search code examples
haskellparsec

Parsec negative match


parseIdent :: Parser (String)
parseIdent = do
  x <- lookAhead $ try $ many1 (choice [alphaNum])
  void $ optional endOfLine <|> eof
  case x of
    "macro" -> fail "illegal"
    _ -> pure x

I'm trying to parse an alphanumeric string that only succeeds if it does not match a predetermined value (macro in this case).


However the following is giving me an error of:

*** Exception: Text.ParserCombinators.Parsec.Prim.many: combinator 'many' is applied to a parser that accepts an empty string.

Which does not make sense, how does many1 (choice [alphaNum]) accept an empty string?


This error goes away if i remove the lookAhead $ try. But it 'fails' with illegal:

...
*** Exception: (line 6, column 36):
unexpected " "
expecting letter or digit or new-line
illegal

Am I going about this correctly? Or is there another technique to implement a negative search?


Solution

  • You almost have it:

    import Text.Parsec
    import Text.Parsec.Char
    import Text.Parsec.String
    import Control.Monad
    
    parseIdent :: Parser (String)
    parseIdent = try $ do
      x <- many1 alphaNum
      void $ optional endOfLine <|> eof
      case x of
        "macro" -> fail "illegal"
        _ -> pure x
    

    So, why didn't your code work?

    • the try is in the wrong spot. The real backtracking piece here is backtracking after you've gotten back your alphanumeric word and checked it isn't "macro"
    • lookAhead has no business here. If you end up with the word you wanted, you do want the word to be consumed from the input. try already takes care of resetting your input stream to its previous state