Question

Suppose there is some parser:

valid :: Parser String
valid = string "valid" <* skipWhile (/= '\n')

It could be used for getting "valid" strings from the multiline text:

> parseOnly (many $ valid <* optional endOfLine) "valid\nvalid\nvalid"
Right ["valid","valid","valid"]

If there is a line where valid Parser fails further text will not be parsed at all:

> parseOnly (many $ valid <* optional endOfLine) "valid\ninvalid\nvalid"
Right ["valid"]

How to get Rigth["valid", "valid"] instead? I thought that try could be helpful here somehow, but not sure how to continue parsing from the the next line.

Was it helpful?

Solution

Using parsec:

-- parser for the rest of the line
rest = manyTill anyChar (eof <|> char '\n' *> return ()) <* optional (char '\n')

-- change this to accept lines, but Just the valid ones
valid :: Parser (Maybe String)
valid = (Just <$> string "valid" <|> const Nothing <$> anyChar) <* rest

-- filter out Nothing
valids = catMaybes <$> many valid

-- Run
*Foo> runParser valids () "input" "valid1\ninvvalid2\nvalid3"
Right ["valid","valid"]
*Foo> runParser valids () "input" "valid1\nvalid2\nvalid3"
Right ["valid","valid","valid"]

Here i have to make a buggy hack: const Nothing <$> anyChar so valid consumes always at least something, otherwise I can't give it to many. However using Maybe you can rewrite your parser as you need (eg making newline mandatory)

Very similar approach is applicable with attoparsec, sorry for spoiling the joy of making it yourself.

{-# LANGUAGE OverloadedStrings #-}
import Data.Attoparsec.Text
import Control.Applicative
import Data.Maybe
import Data.Text

-- parser for the rest of the line
rest = skipWhile (/= '\n') <* optional endOfLine

-- change this to accept lines, but Just the valid ones
valid :: Parser (Maybe Text)
valid = (Just <$> string "valid" <|> const Nothing <$> anyChar) <* rest

-- filter out Nothing
valids = catMaybes <$> many valid
*Main> parseOnly valids "valid1\nvalid2\nvalid3"
Right ["valid","valid","valid"]
*Main> parseOnly valids "valid1\ninvalid2\nvalid3"
Right ["valid","valid"]
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top