问题
import Data.Attoparsec.Text.Lazy
import Data.Text.Lazy.Internal (Text)
import Data.Text.Lazy (pack)
data List a = Nil | Cons a (List a)
list :: Text
list = pack $ unlines
[ "0"
, "1"
, "2"
, "5"
]
How can List Int
parser coud be implemented to parse Cons 0 (Cons 1 (Cons 2 (Cons 5 Nil)))
from list
?
ps: pure parser without parsing a [Int]
and converting it to List Int
is preferable.
回答1:
Like this:
import Control.Applicative
-- rest of imports as in question
data List a = Nil | Cons a (List a)
deriving Show -- for testing
-- definition of list as in question
parseList :: Parser (List Int)
parseList = foldr Cons Nil <$> many (decimal <* endOfLine)
Testing in GHCi:
*Main> parse parseList list
Done "" Cons 0 (Cons 1 (Cons 2 (Cons 5 Nil)))
回答2:
Without converting it from a list of ints:
import Data.Attoparsec.Text.Lazy
import Data.Text.Lazy (Text, pack)
import Control.Applicative
data List a = Nil | Cons a (List a)
deriving Show
input :: Text
input = pack $ unlines [ "0", "1", "2", "5"]
list :: Parser (List Int)
list = cons <|> nil
where
cons = Cons <$> (decimal <* endOfLine) <*> list
nil = pure Nil
main = print $ parse list input
As you can see, the list parser almost looks exactly like the datatype it's parsing.
回答3:
I'd say we can do this by examining many':
many' :: (MonadPlus m) => m a -> m [a]
many' p = many_p
where
many_p = some_p `mplus` return []
some_p = liftM2' (:) p many_p
We can make our own variant similarly:
many'' :: (MonadPlus m) => m a -> m (List a)
many'' p = many_p
where
many_p = some_p `mplus` return Nil
some_p = liftM2 Cons p many_p
and apply it on any monadic parser.
(Note that many'
uses its own liftM2' that is strict in the result of the first action. It's not exported by the module so I used an ordinary liftM2
.)
Or we can make a more general variant that uses Alternative
:
many'' :: (Alternative f) => f a -> f (List a)
many'' p = many_p
where
many_p = some_p <|> pure Nil
some_p = Cons <$> p <*> many_p
回答4:
As others have pointed out, you don't actually need to use recursion (although you can) to parse the list. But if you have a recursive grammar to parse, you can either use recursion in the parser (see bzn's answer and Petr's answer) or you can recurse on the result of the parser (for something like the nesting you see in Markdown). The latter I covered here: http://www.youtube.com/watch?v=nCwG9ijQMuQ&t=17m32s
来源:https://stackoverflow.com/questions/14674047/parse-recursive-data-with-parsec