如何在不消耗的情况下使Attoparsec解析器成功(如parsec lookAhead)

Jim*_*ffa 5 parsing haskell attoparsec

我写了一个快速的attoparsec解析器来遍历一个aspx文件并删除所有的样式属性,它的工作正常,除了它的一部分,我无法弄清楚如何使它成功匹配>而不消耗它.

这就是我所拥有的:

anyTill = manyTill anyChar
anyBetween start end = start *> anyTill end

styleWithQuotes = anyBetween (stringCI "style=\"") (stringCI "\"")
styleWithoutQuotes = anyBetween (stringCI "style=") (stringCI " " <|> ">")
everythingButStyles = manyTill anyChar (styleWithQuotes <|> styleWithoutQuotes) <|> many1 anyChar
Run Code Online (Sandbox Code Playgroud)

我理解这部分是因为我在everythingButStyles中如何使用manyTill,这就是我如何积极地放弃所有样式的东西,但是styleWithoutQuotes我需要它来匹配">"作为结束,但不要消耗它,在parsec中我会做,lookAhead ">"但我不能在attoparsec做到这一点.

Dan*_*her 5

同时,在attoparseclookAhead中添加了组合,所以现在可以使用lookAhead (char '>')lookAhead (string ">")来实现目标。

以下是其引入之前的解决方法。


您可以使用 构建您的非消费解析器peekWord8,它只查看下一个字节(如果有)。由于ByteString有一个Monoid实例,Parser ByteString是 a MonadPlus,您可以使用

lookGreater = do
    mbw <- peekWord8
    case mbw of
      Just 62 -> return ">"
      _ -> mzero
Run Code Online (Sandbox Code Playgroud)

(62 是 ) 的代码点,'>'要么在'>'不消耗它的情况下找到它,要么失败。

  • 你会在最后找回你的灵魂。略有转变。 (4认同)

And*_*ewC 5

anyBetween start end = start *> anyTill end
Run Code Online (Sandbox Code Playgroud)

你的anyBetween解析器会吃它的最后一个字符,因为anyTill它 - 它被设计为解析一个结束标记,但假设你不想让输入中的右括号再次解析.

请注意,您的end解析器都是单字符解析器,因此我们可以更改功能以使用它:

anyBetween'' start ends = start *> many (satisfy (not.flip elem ends))
Run Code Online (Sandbox Code Playgroud)

但是many效率不如Attoparsec takeWhile,你应该尽可能多地使用它,所以如果你已经完成了

import qualified Data.Attoparsec.Text as A
Run Code Online (Sandbox Code Playgroud)

然后

anyBetween' start ends = start *> A.takeWhile (not.flip elem ends)
Run Code Online (Sandbox Code Playgroud)

应该做的伎俩,我们可以重写

styleWithoutQuotes = anyBetween' (stringCI "style=") [' ','>']
Run Code Online (Sandbox Code Playgroud)

如果你想要它吃' '但不是'>'你可以事后明确地吃空间:

styleWithoutQuotes = anyBetween' (stringCI "style=") [' ','>'] 
                     <* A.takeWhile isSpace
Run Code Online (Sandbox Code Playgroud)

追求更多 takeWhile

或许styleWithQuotes也可以用重写来使用takeWhile,所以让我们做两个帮助anyBetween.它们从起始解析器到结束字符,并且有包容性和独占版本:

fromUptoExcl startP endChars = startP *> takeTill (flip elem endChars)
fromUptoIncl startP endChars = startP *> takeTill (flip elem endChars) <* anyChar
Run Code Online (Sandbox Code Playgroud)

但我想从你所说的,你想styleWithoutQuotes成为一个混合体; 它吃' '但不吃>:

fromUptoEat startP endChars eatChars = 
            startP 
            *> takeTill (flip elem endChars) 
            <* satisfy (flip elem eatChars)
Run Code Online (Sandbox Code Playgroud)

(所有这些都假设您的结束字符列表中有少量字符,否则elem效率不高 - Set如果您检查字母表之类的大列表,则会有一些变体.)

现在重写:

styleWithQuotes' = fromUptoIncl (stringCI "style=\"") "\""
styleWithoutQuotes' = fromUptoEat (stringCI "style=") " >" " "
Run Code Online (Sandbox Code Playgroud)

整体解析器

everythingButStyles使用<|>方式意味着如果它没有找到"style"它将回溯然后采取一切.这是一个可能很慢的事情的例子.问题是我们迟到了 - 在输入字符串的末尾,这是一个关于我们是否应该失败的选择的不好时机.让我们全力以赴,努力

  1. 如果我们要失败,马上就失败了.
  2. 最大限度地利用Data.Attoparsec.Text.Internal中更快的解析器

想法:直到我们得到一个s,然后跳过风格,如果那里有一个.

notStyleNotEvenS = takeTill (flip elem "sS") 
skipAnyStyle = (styleWithQuotes' <|> styleWithoutQuotes') *> notStyleNotEvenS 
               <|> cons <$> anyChar <*> notStyleNotEvenS
Run Code Online (Sandbox Code Playgroud)

anyChar通常是一个sS,但没有感觉再次检查.

noStyles = append <$> notStyleNotEvenS <*> many skipAnyStyle 

parseNoStyles = parseOnly noStyles
Run Code Online (Sandbox Code Playgroud)