3

f# 計算式を使用して構成的再帰降下パーサーを実行する方法についていくつかの例が見つかりましたが、私はそれらを逆に使用しようとしました。一部の XML データから (c++) ソース ファイルを生成するための読みやすいコードを作成する。それでも、私は立ち往生しており、コミュニティが私の誤解を見つけるのを手伝ってくれれば幸いです. 公共の利益のために、私の希望は、この投稿がすぐに f# 計算式、モナディック スタイルを使用してクールな方法でコード ジェネレーターを実行する方法を紹介することです。

これまでに私が得た距離は次のとおりです(簡略化して、この質問の目的で世代の入力データを省略しています):

// in my full fledged application, State type also contains the Input data, used for generating code.
type State() = 
    let builder = new System.Text.StringBuilder()
    let mutable indentLevel : int = 0
    member this.Result() = builder.ToString()
    member this.Emit (s : string) : unit = builder.Append( s )
    // ... Methods allowing to do the indenting right, using indentLevel. And adding Output to the builder instance.
    member this.Indent() = indentLevel <- indentLevel + 1
    member this.Exdent() = indentLevel <- indentLevel - 1
// The return value of the Formatters is State only to allow for |> pipelining.
type Formatter = State -> State
type FormatterBuilder() = 
    // Q: Bind() Kind of Looks wrong - should it be a generic, taking one generic first Parameter? See Class function below.
    member this.Bind (state,formatter) = formatter state
    member this.Return state = state              // Q: Not sure if this is the way to go. Maybe some Lambda here?!

let format = new FormatterBuilder()

// Q: Now Comes the part I am stuck in!
// I had the idea to have a "Block" function which 
// outputs the "{", increases the indent Level, 
// invokes the formatters for the Content of the block, 
// then reduces the indent Level, then Closes "}". 
// But I have no idea how to write this.
// Here my feeble attempt, not even sure which Parameters this function should take.
let rec Block (formatters : Formatter list) (state : State) : State =
    format 
        {
            state.EmitLine("{") // do I Need a "do!" here?
            state.Indent()
            formatters |> List.iter (fun f -> do! f state) // Q: "state" is not really propagated. How to do this better?
            state.Exdent()
            state.EmitLine "}"
        }
// Functions with "Get" prefix are not shown here. They are supposed to get the Information
// from the Input, stored in State class, which is also not shown here.
let rec Namespace (state : State) : State =
    format
        {
             state.EmitLine(GetNameSpace state)
        }
let rec Class (classNode : XmlNode) (state : State) : State =
     Format
        { 
             do! TemplateDecl classNode state   // TemplateDecl function not shown in sample code
             do! ClassDecl classNode state
             do! Block [ NestedTypes classNode; Variables classNode; // ... ] // just to give the idea. Q: the list seems wrong here - how to do it better? 
        }
let GenerateCode() : string = 
     let state = new State()
     format
         {
             do! Namespace state    // Q: Is there a way to get rid of the passing of state here?
             do! Block 
                [   // Q: Maybe a Seq is better than a list here?
                 for c in State.Classes do // Q: requires override of a few functions in Builder class, I guess?!
                  do! Class c state
                ]
         }    
     state.Result()

明らかに、上記のコードはせいぜい私が達成しようとしていることを示しているだけです。私の調査では、計算式の使用方法に関する良い例は得られませんでした。私が見つけた多くの例は、ビルダーがどのように宣言されているかを示すか、少し後で停止しますが、最終的な式を実際に記述する方法を示していません。

したがって、誰かが上記の意味不明なコードがやろうとしていることを実行する実際のサンプルを投稿する時間を見つけた場合、それは最も有益であり、この (少なくとも私にとっては) 紛らわしい側面に関してインターネットで見つけることができるもののギャップを埋めるでしょう. f# プログラミングの。

上記のコード サンプルでは、​​そもそもビルダー モナドから取得したものを確認できません。フォーマッタ コードは、非モナディック実装に比べてきれいに見えません。

誰かが回答の投稿でパラメーターに署名と型を追加してくれれば、それは素晴らしいことです。少なくとも私にとっては、「let-the-compiler-find-the-types」スタイルに比べてはるかに理解しやすいです。

4

1 に答える 1