2011-07-20 15 views
5

FParsecを使用して標準的な単純型(ラムダ計算の意味で)を解析しようとしていますが、FParsecで使用されているLex/Yacc形式から、再帰的な定義に。私が解析しようとしているタイプのFParsecの単純型の解析

例は次の通りである:

  • 〇〇

    • - > O
    • (O - > O - > O) - > O

    そして、ここに私の試みがあります:

    
        type SType = 
         | Atom 
         | Arrow of SType * SType 
    
        let ws = spaces 
        let stype, styperef = createParserForwardedToRef() 
    
        let atom = pchar 'o' .>> ws |>> (fun _ -> Atom) 
    
        let arrow = pipe2 (stype .>> (pstring "->" .>> ws)) 
             stype 
             (fun t1 t2 -> Arrow (t1,t2)) 
    
        let arr = parse { 
           let! t1 = stype 
           do! ws 
           let! _ = pstring "->" 
           let! t2 = stype 
           do! ws 
           return Arrow (t1,t2) 
           } 
    
        styperef := choice [ pchar '(' >>. stype .>> pchar ')'; 
             arr; 
             atom ] 
    
        let _ = run stype "o -> o"` 
    

    これをインタラクティブにロードすると最後の行はスタックオーバーフローを引き起こします(皮肉なことに、これらの日を検索するのは非常に困難です)。なぜ再帰的な参照があるのか​​想像することができますが、先読みの1トークンがstypeの最初の(括弧で囲まれた)選択に続くのを防ぐことができたと思います。したがって、arrを選択する必要があります。これはstypeなどを選択します。しかし、このループを防ぐ方法は?

    私は、ライブラリの慣用的な使用についてのコメントだけでなく、試行した解決策への修正にも興味があります。

  • +0

    :http://stackoverflow.com/questions/6186230/recursive-grammars-in-fparsec –

    +0

    おかげで、私が読んでいるあなたのケースでは、たとえばsepBy1コンビネータを使用することができますその質問/回答、しかし私はかなり私の問題に答えを引き継ぐ方法を見ることはできません。私はもう一度見ていきます。 – rneatherway

    答えて

    4

    あなたはFParsecで作業するときは、代わりに左再帰のsequence combinatorsの助けを借りて、シーケンスを解析する必要があります。これは、あなたが欲しい欲しいことができ

    open FParsec 
    
    type SType = 
        | Atom 
        | Arrow of SType * SType 
    
    let ws = spaces : Parser<unit, unit> 
    let str_ws s = pstring s >>. ws 
    
    let stype, stypeRef = createParserForwardedToRef() 
    
    let atom = str_ws "o" >>% Atom 
    
    let elem = atom <|> between (str_ws "(") (str_ws ")") stype 
    
    do stypeRef:= sepBy1 elem (str_ws "->") 
           |>> List.reduceBack (fun t1 t2 -> Arrow(t1, t2)) 
    
    let _ = run stype "o -> o" 
    
    +0

    私はsepByを忘れています。いい答え! –

    +0

    >>%のようにこれはとても感謝しています。しかし、これは " - >"の右結合性を捕捉しません。 stypeRefの定義を 'chainr1 elem((str_ws" - > ")>>%(fun t1 t2 - > Arrow(t1、t2)))' 'に変更しました。おそらく、あなたは' 'List .reduce'も同様です。 – rneatherway

    +0

    私は 'reduce'を' reduceBack'に置き換え、矢印を右結合演算子として解析しました。私は 'chainr'実装よりも' sepBy'と 'reduceBack'クリーナーを使って簡単な解決策を見つけました。特に削減関数が定数であるためです。矢印は右結合演算子であるため、シーケンスの要素を使って何らかの種類の中間スタックやリストを構築しなければならないので、ここでも 'chainr1'を使うと効率が上がりません。それどころか、解析されたリダクション関数を記録しておく必要があるため、少し遅くする必要があります。 –

    0

    これは実行されますが、あまりに多く一緒にハッキングされている可能性があります。 type Parser...は、コンパイルエラーを避けるためにFParsecのドキュメントにあります。

    type SType = 
        | Atom 
        | Arrow of SType * SType 
    
    type UserState = unit 
    type Parser<'t> = Parser<'t, UserState> 
    
    
    let ws = spaces 
    
    let atom : Parser<_> = pchar 'o' .>> ws |>> (fun _ -> Atom) 
    
    let rec term = 
        parse { 
         // Force something to come before another term. Either 
         // an atom, or a term in parens. 
         let! first = choice [atom; 
              (pstring "(" .>> ws) >>. term .>> 
               (pstring ")" .>> ws)] 
    
         // Check if this is an arrow. If not, just return first. 
         let! res = choice [((pstring "->") .>> ws) >>. term |>> (fun x -> 
               Arrow (first, x)); 
              preturn first] 
         return res 
         } 
    
    関連する問題