7

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
4

1 に答える 1

15

Parsecの戦略は基本的にLL(1)です。これは、入力が消費されるたびに現在のブランチに「コミット」することを意味します。パーサーopenTagは、<を使用しますchar '<'。つまり、の代わりに表示された場合>/新しい選択を試みる代わりに、解析全体が失敗します。openTag入力を消費せずに失敗した場合は、別の選択肢が試されます。Parsecは、効率性(代替手段は指数関数的な時間です!)と妥当なエラーメッセージのためにこれを行います。

2つのオプションがあります。やめるのが合理的である場合の好ましいオプションは、入力を消費せずにすべての選択が行われるように文法を因数分解することです。

tag = word <|> (char '<' >> tagbody)
    where
    tagbody = do
        content <- tagcontent
        choice [ string "/>", char '>' ]

モジュロエラーとスタイル(私の脳は現時点では少し揚げられています:-P)。

パーセクのセマンティクスをローカルで変更するもう1つの方法(前述のエラーメッセージと効率を犠牲にして-しかし、ローカルであるため、通常はそれほど悪くはありません)はtry、パーサーが入力を消費し、それでも「ソフトに失敗する」ことを可能にするコンビネーターを使用することです。 "したがって、別の選択肢を試すことができます。

nulltag = try $ between (char '<') (string "/>") word
-- etc.

tryを使用すると、上記のように因数分解するよりもクリーンで簡単な場合があります。これにより、言語の「深層構造」がわかりにくくなる可能性があります。これは、スタイル上のトレードオフです。

于 2011-04-18T03:40:02.713 に答える