2016-06-13 9 views
3

私のプロジェクトに特定のドメイン固有の言語を実装するパーサーを実装しています。OperatorPrecedenceParserをオプション(opt)式として解析する

私が難しかったのは、表現全体(FParsecのOperatorPrecedenceParserを使用して実装されている)を作成して、式全体をオプションにすることです。

私は、ネット上の多くの例と同じように、パーサーOPPを実装しました。また、行末コメントのために空白がどこで使われているのかを調べてみました。私の試みはボットケースでは動作しないようです(式と行末コメントの両方がオプションです)。

具体的には(以下の例で実装されているように)、次の構文解析に成功しようとしています。

KeyValue: expression # comment 
KeyValue: 
KeyValue: # comment 

ここで、expressionはオプションであり、式の後にオプションのコメントがあります。 "KeyValue:"はこの例ではハードコードされていますが、私が持っているメインのパーサーコードでは識別子です。

N.B.以下のサンプルについて。私は2つの浮動小数点の単純なインフィックスの追加を解析する最小限の式を実装しました。完全なパーサーはもっと多くを実装します。

N.B.2。式は:と式の間に最低1空白を持たなければなりません。 #コメント文字は、式にはまったく現れません。

「オプション」とはどのように表現できますか。以下のeKeyValue FParsec Parser型を参照してください。

以下の例には6つの例が含まれています。すべてのケースが機能するはずです。私は条件の最後にwsBeforeEOLを追加しようとしましたが、オプションの式(オプション)はありますが、何も動作していないようです。 OPPは常に消費され、決して失敗しないように見えます。

以下のサンプルプログラムの出力は次のようになります。

はサンプルプログラムがある
Test1 - Failure: 
Error in Test1 - No Expression but comment: Ln: 1 Col: 15 
    KeyName: # No Expression but comment 
      ^
Expecting: floating-point number or '(' 

Test2 - Success: Key (Some (Arithmetic (Number 2.0,Plus,Number 2.0))) 
Test3 - Success: Key (Some (Arithmetic (Number 3.0,Plus,Number 3.0))) 
Test4 - Success: Key (Some (Arithmetic (Number 3.0,Plus,Number 4.0))) 
Test5 - Success: Key (Some (Arithmetic (Number 4.0,Plus,Number 4.0))) 
Test6 - Success: Key null 
Press any key to continue . . . 

(上記のテストケースは、main()関数にあります。

open FParsec 


    // Placeholder for state... 
    type UserState = 
     { 
      dummy: int    
     } 
     with 
      static member Create() = {dummy = -1} 


    type Operator = 
     | Plus 

    type Expression = 
     | Number of float 
     | Arithmetic of Expression * Operator * Expression // Composes 2 primatives 

    type Statement = 
     | Key of Expression option // Optional expression name 


// very simple parsers which handles a simple string on one line. 
// White space handling 
let isBlank = fun c -> c = ' ' || c = '\t' 
let ws1 = skipMany1SatisfyL isBlank "whitespace" 
let ws = skipManySatisfy isBlank 
let comment = pstring "#" >>. skipRestOfLine false 
let wsBeforeEOL = skipManySatisfy isBlank >>. optional comment 

// Parse a number 
let sNumber = pfloat .>> wsBeforeEOL |>> Number // Test wsExpression ending 

// The expression reference 
let expression, expressionRef = createParserForwardedToRef() 

let expressionFragment = choice [sNumber] //;sBool;sNull;sString;sIdentifier] 
let bracketedExpressionFragment = between (pstring "(" .>> ws) (pstring ")" .>> ws) expression 

// The parser for addition only 
let oppa = new OperatorPrecedenceParser<Expression, unit, UserState>() 

let parithmetic = oppa.ExpressionParser 

//oppa.TermParser <- (expressionFragment .>> wsBeforeEOL) <|> (bracketedExpressionFragment .>> wsBeforeEOL) 
oppa.TermParser <- choice[expressionFragment;bracketedExpressionFragment] 
oppa.AddOperator(InfixOperator("+", ws, 1, Associativity.Left, fun x y -> Arithmetic(x, Plus, y))) 
expressionRef := oppa.ExpressionParser 


// *** HERE: Define the Key, with optional expression, which must have at lease 1 WS,, then followed by wsBeforeEOL, which is multiple blanks and optional comment. 
let eKeyValue = pstringCI "KeyName:" >>. (opt (ws1 >>. expression)) .>> wsBeforeEOL |>> Key 

// Define the parser for the whole string...in this case a single line 
let htmlProgramParser = spaces >>. eKeyValue .>> spaces .>> eof 

// test harnes on a string 
let parseHtmlProgramString programName str = 
    runParserOnString htmlProgramParser (UserState.Create()) programName str //with 

[<EntryPoint>] 
let main argv = 
    printfn "%A" argv 

    let test1 = 
     " KeyName: # No Expression but comment" 
     |> parseHtmlProgramString "Test1 - No Expression but comment" 
     |> printfn "Test1 - %A" 

    let test2 = 
     " KeyName: 2+2 # Expression and Comment" 
     |> parseHtmlProgramString "Test2 - 2+2 # Expression and Comment" 
     |> printfn "Test2 - %A" 

    let test3 = 
     " KeyName: 3 + 3 # # Expression and Comment2" 
     |> parseHtmlProgramString "Test3 - 3 + 3 # Expression and Comment2 (Spaces)" 
     |> printfn "Test3 - %A" 

    let test4 = 
     " KeyName: (3 + 4) # Bracketed Expression and Comment" 
     |> parseHtmlProgramString "Test4 - (3 + 4) # Bracketed Expression and Comment" 
     |> printfn "Test4 - %A" 

    let test5 = 
     " KeyName: (4 + 4) " 
     |> parseHtmlProgramString "Test5 - (4 + 4) # Expression + <no comment>" 
     |> printfn "Test5 - %A" 

    let test6 = 
     " KeyName:" 
     |> parseHtmlProgramString "Test6 - <no expression> <no comment>" 
     |> printfn "Test6 - %A" 

    0 // return an integer exit code 

答えて

1

は、私はあなたの質問を理解していれば正しく、問題は、eKeyValueの中のws1 >>. expressionが空白を消費した後に失敗する可能性があり、コンビネータが成功するのを防ぐ。

あなたは

let eKeyValue = pstringCI "KeyName:" >>. (opt (ws1 >>? expression)) .>> wsBeforeEOL |>> Key 

ようeKeyValueを定義したり、作品

let eKeyValue = pstringCI "KeyName:" >>. (ws1 >>. opt expression <|>% None) .>> wsBeforeEOL |>> Key 
+0

のようにそれをリファクタリングなどによってこの問題を解決することができます。私はFParsec(そして一般的にはF#)の新機能です。私はそれが私の大きなDSLで私の問題を解決すると信じています。迅速なご協力ありがとうございます。 – Nathan

+0

実際問題はOPPが正しく終了していないことが多いですが。 >>を指摘してくれてありがとう?コンビネータを<|>%含有する。 – Nathan

関連する問題