5
import Text.ParserCombinators.Parsec

delimiter :: Parser ()
delimiter = do char '|'
               return ()
          <?> "delimiter"


eol :: Parser ()
eol = do oneOf "\n\r"
         return ()
    <?> "end of line"

item :: Parser String
item = do entry <- manyTill anyChar (try eol <|> try delimiter <|> eof)
          return entry

items :: Parser [String]
items = do result <- many item
           return result

上記のコードを実行するparseTest items "a|b|c"と、次のエラーが発生します。

*** Exception: Text.ParserCombinators.Parsec.Prim.many: 
combinator 'many' is applied to a parser that accepts an empty string.

eof私はそれがand と関係があると信じています.many itemを削除するeofと、行が で終わらない限り動作させることができますeof.

使用できることはsepByわかっていますが、興味があるのは、このコードが機能しない理由と、それを機能させる方法です。

4

2 に答える 2

6

many空の文字列を受け入れるパーサーには、次のようなパーサーを実際に適用することはできません。これは、文法があいまいになるためです。異なる数値を選択すると、異なる解析結果になる可能性があります...

many itemそれが問題のある組み合わせであると考えるのは正しいです。Anitemは で定義されmanyTillます。(エクスカーション:ところで、次のように単純化できmanyTillます

item :: Parser String
item = manyTill anyChar (eol <|> delimiter <|> eof)

3 つのパーサーはそれぞれ異なる最初のトークンを予期するため、doまたは も必要ありません。) したがって、パーサーは任意の数の文字を解析し、その後に、、または のいずれかが続きます。現在、実際には成功すると少なくとも 1 キャラクターを消費しますが、そうではありません。パーサーは入力の最後で成功しますが、複数回適用できます。例えば、returntrymanyTilleoldelimitereofeoldelimitereofeof

ghci> parseTest (do { eof; eof }) ""
()

入力を消費しないため、item(入力の最後にある)空の文字列で成功することが可能になり、あいまいさが生じます。

これを修正するには、実際に文法を書き直して のようなものに移動するか、通常の s (エンドマーカーとして許可されていない場所) を final (許可されている場所)sepByと区別しようとすることができます。itemeofitemeof

于 2013-11-11T07:05:33.040 に答える
0

これは、空の文字列を次のように解析する方法が無数にあるためです。many emptyString

于 2013-11-11T06:25:24.183 に答える