我试图使用attoparsec创建一个简单的解析器。制作规则如下:
block: ?token> [inline]
inline: <?token>foo<?> | anyText
所以,我试图得到的是,一个块以文字?开头,后跟一个标记,后跟一个&gt;,然后是一系列内联。
内联可以是foo形式的序列,也可以是任何纯文本。
我正在使用爆炸性内存,但我不确定如何将解析器用于避免它。我写作的解析器的意思是提取那些“令牌”#39;的东西。这是我的实施:
import Control.Applicative
import Control.Monad
import Data.Attoparsec.Text as Text
import Data.Text
blockLine :: Parser [Text]
blockLine = do
block <- hiddenBlock -- the block token
inlines <- many (hiddenInline <|> inline) -- followed by inlines, which might have tokens
return $ block : inlines
inline = manyTill anyChar (hiddenInline <|> (endOfInput >> return Text.empty))
hiddenInline = Text.pack <$> do
char '<' -- opening "tag"
char '?' -- opening "tag" still
token <- manyTill anyChar (char '>') -- the token
manyTill anyChar (string "<?>") -- close the "tag"
return token
hiddenBlock = Text.pack <$> do
char '?'
manyTill anyChar (char '>')
对我而言,这看起来是将生产规则非常简单地翻译成LL解析器。我认为困难在于我不确定如何表达内联的制作。它应该是随意的&#34;文本,但解析应在找到hiddenInline后立即停止。
答案 0 :(得分:2)
问题在于您在使用中嵌套了对manyTill
的调用
many
。
由于inline
的终止条件为endOfFile
,manyTill anyChar
将乐于消耗你所有的输入,然后成功。
inline
的后续使用也将成功,因为manyTill
可以运行它
第一个解析器零或更多次。
因此,在many
解析器上使用inline
只会导致many
成功
在生成无限的空字符串列表时永远循环。
使用此示例
parseOnly (many (manyTill anyChar endOfInput)) $ Text.pack ""
大量的分配可能是由attoparsec
积累造成的
继续管理回溯。
作为一般规则,您提供给many
的任何解析器都不应该
平凡地成功(即没有消耗任何输入流)。
因此,您需要重写inline
或以其他方式重组您的
解析器以避免这种情况。