Consider the usage of these different parser combinators.
import Control.Applicative.Combinators
import Text.Regex.Applicative
main :: IO ()
main = do
let parser1 = sym '"' *> manyTill anySym (sym '"')
print $ match parser1 "\"abc\""
let parser2 = sym '"' *> many anySym <* sym '"'
print $ match parser2 "\"abc\""
import Control.Applicative.Combinators
import Text.ParserCombinators.ReadP hiding(many, manyTill)
main :: IO ()
main = do
let parser1 = char '"' *> manyTill get (char '"')
print $ readP_to_S parser1 "\"abc\""
let parser2 = char '"' *> many get <* char '"'
print $ readP_to_S parser2 "\"abc\""
{-# LANGUAGE OverloadedStrings #-}
import Control.Applicative.Combinators
import Data.Attoparsec.Text hiding(manyTill)
main :: IO ()
main = do
let parser1 = char '"' *> manyTill anyChar (char '"')
print $ parseOnly parser1 "\"abc\""
let parser2 = char '"' *> many anyChar <* char '"'
print $ parseOnly parser2 "\"abc\""
import Control.Applicative.Combinators
import Text.Megaparsec hiding(many, manyTill)
import Data.Void
main :: IO ()
main = do
let parser1 = single '"' *> manyTill anySingle (single '"') :: Parsec Void String String
print $ parseMaybe parser1 "\"abc\""
let parser2 = single '"' *> many anySingle <* single '"' :: Parsec Void String String
print $ parseMaybe parser2 "\"abc\""
With all four of them, the manyTill
parser successfully matches abc
, since this doesn't depend on backtracking. With regex-applicative
and ReadP
, the many
parser also successfully matches abc
, since they both backtrack by default. With megaparsec
, the many
parser fails to match, since it doesn't backtrack by default. So far, everything makes sense. However, with attoparsec
, the many
parser fails to match, even though it does backtrack: its documentation says "attoparsec parsers always backtrack on failure" and "if you feed incremental input to an a parser, it will require memory proportional to the amount of input you supply. (This is necessary to support arbitrary backtracking.)". Why is this? Isn't that supposed to be exactly what backtracking is?
attoparsec
only backtracks when the left side of<|>
consumes some characters and then fails. If the left side of<|>
succeeds, unlikeReadP
andregex-applicative
,attoparsec
will never try the right side, even if the parse fails later." – Kopple