non*_*ont 7 xml haskell parsec
在查看Real World Haskell中的CSV示例代码之后,我尝试构建一个小的XML解析器.但是关闭标签会因"意外"/"错误而错误输出.你能告诉我为什么我的"closeTag"解析器不起作用(或者可能不会被调用)吗?谢谢!
import Text.ParserCombinators.Parsec
xmlFile = manyTill line eof
line = manyTill tag eol
eol = char '\n'
word = many1 (noneOf "></")
tag = choice [openTag, closeTag, nullTag, word]
nullTag = between (char '<') (string "/>") word
closeTag = between (string "</") (char '>') word
openTag = between (char '<') (char '>') tagContent
attrval = between (char '"') (char '"') word
atts = do {
(char ' ')
; sepBy attr (char ' ')
}
attr = do {
word
; char '='
; attrval
}
tagContent = do {
w <- word
; option [] atts
; return w
}
parseXML :: String -> Either ParseError [[String]]
parseXML input = parse xmlFile "(unknown)" input
main =
do c <- getContents
case parse xmlFile "(stdin)" c of
Left e -> do putStrLn "Error parsing input:"
print e
Right r -> mapM_ print r
Run Code Online (Sandbox Code Playgroud)
luq*_*qui 15
Parsec的策略本质上是LL(1),这意味着只要消耗了任何输入,它就会"提交"到当前分支.你的openTag解析器<使用它char '<',这意味着如果它看到>而不是/,整个解析失败而不是尝试新的选择.如果openTag没有消耗任何输入并且失败,则会尝试另一种选择.Parsec这样做是为了提高效率(替代方法是指数时间!)和合理的错误消息.
你有两个选择.当合理推出时,首选的选项是将语法分解,以便在不消耗输入的情况下进行所有选择,例如:
tag = word <|> (char '<' >> tagbody)
where
tagbody = do
content <- tagcontent
choice [ string "/>", char '>' ]
Run Code Online (Sandbox Code Playgroud)
Modulo错误和风格(我的大脑现在有点油炸:-P).
另一种方式,它本地改变了parsec的语义(以牺牲前面提到的错误消息和效率为代价 - 但它通常不是太糟糕,因为它是本地的),是使用try组合器,它允许解析器消耗输入并仍然"轻轻地"失败"所以可以尝试另一种选择:
nulltag = try $ between (char '<') (string "/>") word
-- etc.
Run Code Online (Sandbox Code Playgroud)
有时使用try比上面的因子更清晰,更容易,这可能会掩盖语言的"深层结构".这是一种风格权衡.