Parse recursive data with parsec

偶尔善良 提交于 2019-12-12 12:20:44

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!