2

FParsec で同種の json のような配列を解析しようとすると問題が発生します。問題を再現する短い例に分解しました。

#r @"..\packages\FParsec.1.0.2\lib\net40-client\FParsecCS.dll"
#r @"..\packages\FParsec.1.0.2\lib\net40-client\FParsec.dll"

open System
open FParsec

let test p str =
        match run p str with
        | Success(result, _, _)   -> printfn "Success: %A" result
        | Failure(errormsg, _, _) -> printfn "Failure: %s" errormsg


type CValue = CInt of int64
            | CBool of bool
            | CList of CValue list

let P_WHITESPACE = spaces
let P_COMMA = pstring ","
let P_L_SBRACE = pstring "[" .>> P_WHITESPACE
let P_R_SBRACE = P_WHITESPACE >>. pstring "]"

let P_INT_VALUE = pint64 |>> CInt

let P_TRUE = stringReturn "true" (CBool true)
let P_FALSE = stringReturn "false" (CBool false)
let P_BOOL_VALUE = P_TRUE <|> P_FALSE


let P_LIST_VALUE =
    let commaDelimitedList ptype = sepBy (ptype .>> P_WHITESPACE) (P_COMMA .>> P_WHITESPACE)
    let delimitedList = (commaDelimitedList P_INT_VALUE) <|> (commaDelimitedList P_BOOL_VALUE)
    let enclosedList = between P_L_SBRACE P_R_SBRACE delimitedList
    enclosedList |>> CList

関数を使用しtestて試してみると、次の結果が得られます。

test P_LIST_VALUE "[1,2,3]"
Success: CList [CInt 1L; CInt 2L; CInt 3L]

test P_LIST_VALUE "[true,false]"
Failure: Error in Ln: 1 Col: 2
[true,false]
 ^
Expecting: integer number (64-bit, signed) or ']'

演算子を使用するときにP_INT_VALUEとの順序を入れ替えると、解析は成功しますが、同様のエラーで失敗します。基本的に、私が最初に使用するパーサーは、使用しようとするものです。P_BOOL_VALUE<|>[true,false][1,2,3]

LHS がユーザー状態を変更した場合、オペレーターが RHS パーサーを試行しないことは理解して<|>いますが、それがどのように発生するのかわかりません。P_BOOL_VALUE と P_INT_VALUE には共通の開始文字がないため、間違ったデータ型を解析しようとすると、両方ともすぐに失敗するはずです。Int が「false」または「true」で始まることはなく、bool が数字で始まることもありません。

私は何を間違っていますか?

4

1 に答える 1

2

ああ、私はそれを理解しました。エラー メッセージのヒントはor ']'. 問題は、sepBy空の入力で成功するため、 にヒットするtと、空のリストで正常に戻り、制御が に戻りbetween、終了する を見つけようとして失敗すること]です。

解決策は、次のように、空リストのケースを int/bool 固有のパーサーから移動することです。

let P_LIST_VALUE =
    let commaDelimitedList ptype = sepBy1 (ptype .>> P_WHITESPACE) (P_COMMA .>> P_WHITESPACE)
    let delimitedList = (commaDelimitedList P_INT_VALUE) <|> (commaDelimitedList P_BOOL_VALUE) <|> preturn []
    let enclosedList = between P_L_SBRACE P_R_SBRACE delimitedList
    enclosedList |>> CList

sepBy1の代わりに を使用し、 で空のケースを 1 回だけ処理するために をsepBy追加していることに注意してください。<|> preturn []delimitedList

補足として、私はあなたの正確なアプリケーションを知りませんが、一般的にパーサーで入力を強制することはあまり良い考えではありません。これを実装するより一般的な方法は、commaDelimitedList (P_INT_VALUE <|> P_BOOL_VALUE)(元の を使用してcommaDelimitedList) a を解析し、その後の分析フェーズで型をチェックすることです。

于 2016-09-13T10:46:41.717 に答える