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))

注释块和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

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

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)))

最新更新