haskellparsec

Conditions in Parsec


If I have a parser than reads a string of numbers separated by spaces into a list of Ints, how do I handle a trailing space? At the moment I have:

row :: Parser [Int]
row  = do
  optional spaces
  f <- (many (oneOf "0123456789"))
  r <- ((char ' ') >> row) <|> pure []
  pure (read f:r)

Which works fine with a string that does not have a trailing space but fails with a trailing space.

>λ= parse row "" " 2  0 12  3     7"
Right [2,0,12,3,7]

>λ= parse row "" " 2  0 12  3     7  "
Right [2,0,12,3,7,*** Exception: Prelude.read: no parse

What is the solution to this problem and more so, how would I have a condition where if '\n' is consumed then the parser returns []

EDIT: From reading @amalloy's answer and the parsec source code, I thought it useful to add a version that works here (although, @amalloy's advice to not try and roll existing functions makes more sense)

row :: Parser [Int]
row = do
  spaces
  f <- (read <$> many1 digit)
  do
    many1 $ char ' '
    r <- row
    pure (f:r) <|> pure [x]
  <|> pure []

Solution

  • Instead of implementing all this low-level stuff yourself, I suggest just using sepEndBy. For example,

    row :: Parser [Int]
    row = spaces *> (int `sepEndBy` many1 space)
      where int = read <$> many1 digit