haskell - Parsec,读取以字符串结尾的文本

标签 haskell parsec blaze-html

我正在努力使用Parsec来解析Google project wiki syntax的一小部分子集。 ,并将其转换为 HTML。我的语法仅限于文本序列和项目列表。这是我想要识别的示例:

Text that can contain any kind of characters,
except the string "\n *"
 * list item 1
 * list item 2

End of list 

到目前为止我的代码是:

import Text.Blaze.Html5 (Html, toHtml)
import qualified Text.Blaze.Html5 as H
import Text.ParserCombinators.Parsec hiding (spaces)

parseList :: Parser Html
parseList = do
    items <- many1 parseItem
    return $ H.ul $ sequence_ items

parseItem :: Parser Html
parseItem = do
    string "\n *"
    item <- manyTill anyChar $
        (try $ lookAhead $ string "\n *") <|>
        (try $ string "\n\n")
    return $ H.li $ toHtml item

parseText :: Parser Html
parseText = do
    text <- manyTill anyChar $
        (try $ lookAhead $ string "\n *") <|>
        (eof >> (string ""))
    return $ toHtml text

parseAll :: Parser Html
parseAll = do
    l <- many (parseUl <|> parseText)
    return $ H.html $ sequence_ l

当将 parseAll 应用于任何字符序列时,我收到以下错误消息:"*** 异常:Text.ParserCombinators.Parsec.Prim.many:组合器 'many' 是应用于接受空字符串的解析器。 我明白这是因为我的解析器 parseText 可以读取空字符串,但我看不到任何其他方式。如何识别由字符串分隔的文本? (此处为“\n *”)。

我也愿意接受有关我使用秒差距的方式的任何评论或建议。我不禁发现我的代码有点难看。我可以用更简单的方式完成这一切吗?例如,由于字符串 "\n *",存在代码复制(这有点痛苦),该字符串用于识别文本序列的结尾、列表项的开头,以及列表项的末尾...

最佳答案

问题是manyTill组合器匹配零个或多个anyChar。只需更改 parseText 以匹配至少一个 anyChar,这样在读取分隔符之一时就会失败 - 不幸的是没有 many1Till组合器。

我也更喜欢parseAll = fmap (H.html . sequence) $ many (parseUl <|> parseText) ,既然你提到了丑陋的秘诀。

parseText = do
               notFollowedBy $ string "\n *"
               first <- anyChar
               rest <- manyTill anyChar $
                       (try $ lookAhead $ string "\n *") <|>
                       (eof >> (string ""))
               return $ toHtml first:rest

parseAll = fmap (H.html . sequence) $ many (parseUl <|> parseText)

也就是说,谷歌上的“parseUl”只给出了这个问题,所以如果不了解该解析器,我不知道更好的解决方案。


由于我对第一个接受的答案感到绝望,我完整地写了它:)只需在上面添加 html 内容与 fmap (首选)或 return 即可。

module Main where
import System.Environment
import Control.Monad
import Text.ParserCombinators.Parsec hiding (spaces)

parseList :: Parser [String]
parseList = many1 parseItem

parseItem :: Parser String
parseItem = string "\n *" >> (manyTill anyChar $ try $ lookAhead $ char '\n')

parseText :: Parser String
parseText = do
               notFollowedBy $ string "\n *" 
               first <- anyChar
               rest <- manyTill anyChar $
                   (try $ lookAhead $  string "\n *") <|>
                   (eof >> (string ""))
               return $ first:rest

parseAll :: Parser [String]
parseAll = many $ parseText <|> fmap concat parseList

parseIt :: String -> String
parseIt input = case parse parseAll "wiki" input of
    Left err -> "No match: " ++ show err
    Right val -> "It worked"

main = do
          args <- getArgs
          putStrLn (parseIt (args !! 0))

我假设列表不能包含换行符,但是try $ lookahead $ char '\n'很容易调整。您可以分解 string "\n *"以避免重复。在这里,我粉碎了所有列表并忽略了序列解析,但您必须更改它。如果您将“文本”分成多行文本,然后仅检查一行文本或列表中的一行,那么一切都会更简单。

关于haskell - Parsec,读取以字符串结尾的文本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20730488/

相关文章:

haskell - 迭代 Haskell 中的列表会阻止吗?

haskell - Haskell 中的 SceneGraph 遍历

haskell - 使用 Parsec,如何解析零个或多个以 foo2 结尾且全部由点分隔的 foo1?

scala - 在 FRP 中实现快照

haskell - 如何抗拒订购?

haskell - 如何在 Text.Parsec.Expr 中制作后缀 (--)

haskell - 如何从 blaze 获取 html -- 打印到文件

html - Blaze-html 类属性链接/附加/串联?

haskell - 如何将 TChan 邮箱附加到线程并使用套接字接收/发送消息?

haskell - 在 Haskell 中从未知序列创建不同类型?