haskell - attoparsec 中的条件先行

标签 haskell attoparsec

假设有一个数据结构表示一个文本,里面有评论。

data TWC
  = T Text TWC -- text
  | C Text TWC -- comment
  | E -- end
  deriving Show

因此字符串像

"Text, {-comment-}, and something else"

可以编码为

T "Text, " (C "comment" (T ", and something else" E))

注释 block 和 E 的解析器非常简单:

twcP :: Parser TWC
twcP = eP <|> cP <|> tP

cP :: Parser TWC
cP = do
  _ <- string "{-"
  c <- manyTill anyChar (string "-}")
  rest <- cP <|> tP <|> eP
  return (C (pack c) rest)

eP :: Parser TWC
eP = do
  endOfInput
  return E

以如此简单的方式实现文本 block 的解析器

tP :: Parser TWC
tP = do
  t <- many1 anyChar
  rest <- cP <|> eP
  return (T (pack t) rest)

由于其贪婪的本性,使其将评论部分作为文本消费

> parseOnly twcP "text{-comment-}"
Right (T "text{-comment-}" E)
it ∷ Either String TWC

那么,问题是如何表达直到输入结束或直到注释部分的解析逻辑?换句话说,如何实现条件先行解析器?

最佳答案

你是对的,有问题的代码是 tP 的第一行,它贪婪地解析文本而不在注释处停止:

tP = do
  t <- many1 anyChar

在解决这个问题之前,我首先想稍微重构一下您的代码以引入助手并使用应用样式,并将有问题的代码隔离到 text 助手中:

-- Like manyTill, but pack the result to Text.
textTill :: Alternative f => f Char -> f b -> f Text
textTill p end = pack <$> manyTill p end

-- Parse one comment string
comment :: Parser Text
comment = string "{-" *> textTill anyChar (string "-}")

-- Parse one non-comment text string (problematic implementation)
text :: Parser Text
text = pack <$> many1 anyChar

-- TWC parsers:

twcP :: Parser TWC
twcP = eP <|> cP <|> tP

cP :: Parser TWC
cP = C <$> comment <*> twcP

eP :: Parser TWC
eP = E <$ endOfInput

tP :: Parser TWC
tP = T <$> text <*> twcP

要实现前瞻,我们可以使用 lookAhead组合器,它在不消耗输入的情况下应用解析器。这使我们能够对 text 进行解析,直到它到达 comment(不使用它)或 endOfInput:

-- Parse one non-comment text string (working implementation)
text :: Parser Text
text = textTill anyChar (void (lookAhead comment) <|> endOfInput)

通过该实现,twcP 的行为符合预期:

ghci> parseOnly twcP "text{-comment-} post"
Right (T "text" (C "comment" (T " post" E)))

关于haskell - attoparsec 中的条件先行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38202349/

相关文章:

haskell - 词法或动态范围 - Haskell 实现的评估器

haskell - Cabal install ghc-mod 提示损坏的软件包 (GHC 7.6.3)

haskell - GHC 无需 Root 即可安装

haskell - 如何编写 attoparsec 的 takeWhile1 的更通用(但高效)版本?

haskell - haskell中的attoparsec或parsec

haskell - Haskell 中的旋转函数

haskell - 使用常量值退化类型类实例声明

haskell - Attoparsec:跳过括号中的术语?

haskell - 解析 attoparsec 中不以某些字符结尾的标识符

parsing - Haskell:遍历字符串/文本文件