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"
]
如何实施List Int
解析器来解析Cons 0 (Cons 1 (Cons 2 (Cons 5 Nil)))
中的list
?
ps :不解析[Int]
并将其转换为List Int
的纯解析器更可取。
答案 0 :(得分:6)
像这样:
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)
在GHCi中进行测试:
*Main> parse parseList list
Done "" Cons 0 (Cons 1 (Cons 2 (Cons 5 Nil)))
答案 1 :(得分:2)
不将其从整体列表中转换出来:
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
如您所见,列表解析器几乎与它正在解析的数据类型完全相同。
答案 2 :(得分:1)
我想我们可以通过检查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
我们可以类似地制作自己的变体:
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
并将其应用于任何monadic解析器。
(请注意,many'
使用自己的liftM2'
,它在第一个操作的结果中是严格的。它不会被模块导出,所以我使用普通的liftM2
。)
或者我们可以制作使用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
答案 3 :(得分:1)
正如其他人所指出的,你实际上并不需要使用递归(尽管你可以)来解析列表。但是如果你有一个递归语法来解析,你可以在解析器中使用递归(参见bzn的回答和Petr的回答),或者你可以解析解析器的结果(比如你在Markdown中看到的嵌套)。我在这里介绍了后者:http://www.youtube.com/watch?v=nCwG9ijQMuQ&t=17m32s