5

私は十分に単純に見えるパーサーを持っています。他のすべてのサブパーサーが失敗したため、一般的な解析エラーに関する情報を提供するために、このサブパーサーを最後に追加しました-

/// Read the rest of a line as an error.
let readError =
    parse {
        let! restOfLineStr = restOfLine true
        return makeViolation ("Read error on: " + restOfLineStr + ".") }

/// Read an expression.
do readExprRef :=
    choice
        [attempt readBoolean
         attempt readCharacter
         attempt readString
         attempt readInt
         attempt readError] // just now added this sub-parser, and get the issue

ただし、選択肢としてreadErrorを追加すると、実行時のストリーム消費に関する恐ろしいFParsecエラーが発生します-The combinator 'many' was applied to a parser that succeeds without consuming input and without changing the parser state in any other way.解析された残りの行を使用して使用済みエラーを作成するため、なぜこれが発生するのかわかりません(ここでは「違反」)構造。

誰かが私がこれを理解するのを手伝ってもらえますか?パーサーエラーを間違った方法でユーザーに通知しようとしていますか?そうでない場合、どうすればこれを修正できますか?

よろしくお願いします!

* より詳しく *

関連する可能性のあるコードをさらにいくつか示します-

/// The expression structure.
type Expr =
| Violation of Expr
| Boolean of bool
| Character of char
| String of string
| Int of int

/// Make a violation from a string.
let makeViolation str = Violation (String str)

/// Read whitespace character as a string.
let spaceAsStr = anyOf whitespaceChars |>> fun chr -> string chr

/// Read a line comment.
let lineComment = pchar lineCommentChar >>. restOfLine true

/// Read a multiline comment.
/// TODO: make multiline comments nest.
let multilineComment =
    between
        (pstring openMultilineCommentStr)
        (pstring closeMultilineCommentStr)
        (charsTillString closeMultilineCommentStr false System.Int32.MaxValue)

/// Read whitespace text.
let whitespace = lineComment <|> multilineComment <|> spaceAsStr

/// Skip any white space characters.
let skipWhitespace = skipMany whitespace

/// Skip at least one white space character.
let skipWhitespace1 = skipMany1 whitespace

/// Read a boolean.
let readBoolean = 
    parse {
        do! skipWhitespace
        let! booleanValue = readStr trueStr <|> readStr falseStr
        return Boolean (booleanValue = trueStr) }

/// Read a character.
let readCharacter =
    parse {
        // TODO: enable reading of escaped chars
        do! skipWhitespace
        let! chr = between skipSingleQuote skipSingleQuote (manyChars (noneOf "\'"))
        return Character chr.[0] }

/// Read a string.
let readString =
    parse {
        // TODO: enable reading of escaped chars
        do! skipWhitespace
        let! str = between skipDoubleQuote skipDoubleQuote (manyChars (noneOf "\""))
        return String str }

/// Read an int.
let readInt =
    parse {
        do! skipWhitespace
        let! value = pint32
        let! _ = opt (skipString intSuffixStr)
        do! notFollowedByLetterOrNameChar
        do! notFollowedByDot
        return Int value }

私は知らないよ。おそらく問題は、readErrorパーサーを実行しようとすると、すでにストリームの最後にあることです。それにより、restOfLineは入力を消費せず、空白さえも消費しませんか?

* 結論 *

readErrorパーサーを使用したエラー報告へのアプローチが間違っていることが判明しました。正しいアプローチは、次のように「最後まで」パーサーを使用することです-

/// Read the end of input.
let readEndOfInput = skipWhitespace >>. eof

// Read multiple exprs.
let readExprs = many readExpr

// Read exprs until the end of the input.
let readExprsTillEnd = readExprs .>> readEndOfInput

ここで、入力ストリーム内のすべてのexprを取得する必要があるときに、readExprsTillEndを実行します。

もう一度ありがとう、グスタボ!

4

1 に答える 1

2

あなたが投稿した追加のコードをありがとう、残念ながら私はエラーを再現することができませんでした。しかし、最後のものを削除してみませんattemptか?それは意味がなく、おそらく問題を引き起こしていると思います。

do readExprRef :=
    choice
        [attempt readBoolean
         attempt readCharacter
         attempt readString
         attempt readInt
         readError]

私はFParsecの専門家ではありませんが、最後に選択したパーサーは試みではないと思います。

アップデート:

readErrorパーサーは、入力を消費しなくても成功します。ある時点でreadExpr、パラメーターとしての呼び出しがある場合、パーサーmanyは終了しません。つまり、あなたが電話する場合

run (many readError) "" ;;

many失敗するまでそのパーサーを適用し続けるため、そのエラーメッセージが表示されますが、失敗することはありません。

http://www.quanttec.com/fparsec/reference/charparsers.html#members.restOfLineでrestOfLine関数の仕様を見てください。これについて警告します。

これを解決する方法はたくさんありますが、パーサーエラーの処理方法を再検討する必要があると思います。

readErrorあなたができることの1つは、関数を取り出して、 readExpr パーサーを呼び出すときに、このように呼び出すことです。

let readExprs = many readExpr .>> eof

そうすることで、eofを強制し、eofの前に選択したパーサーによって処理されないものがある場合、FParsecは自動的に適切なエラーメッセージを生成します。

そして、そのエラーを処理したい場合は、http: //www.quanttec.com/fparsec/users-guide/customizing-error-messages.htmlをご覧ください。

于 2011-12-17T17:16:29.883 に答える