2011-08-06 7 views
11

コード引用をアセンブリにコンパイルする方法があるかどうかを知りたいですか?F#コード引用をアセンブリにコンパイルする方法

私は例えば、Exp<_>オブジェクト上CompileUntyped()またはCompile()を呼び出すことが可能であることを理解:

let x = <@ 1 * 2 @> 
let com = x.Compile() 

はしかし、どのように私は、アセンブリとしてディスクにcomを持続することができますか?

ありがとうございました。

+2

PowerPackがこれをサポートしているかどうかはわかりません。しかし、私はPowerPackの使用をお勧めしません。そのコードは頻繁にバグがあり、遅すぎます。 System.Reflection.Emitを使用して独自のコンパイラまたは評価版をゼロから作成すると、おそらくより良い結果が得られます。また、F#が引用を最適化しないという問題もあります。たとえば、通常のF#コンパイルのCILにあるジャンプ命令ではなく、一連のif/then/elseになります。 – t0yv0

+0

こんにちは、ありがとう、私はそれら(Reflection.Emitあなたが述べたように)それらの代替案を検討すると思います。 – Ncc

+0

これは便利です:http://stackoverflow.com/questions/2682475/converting-f-quotations-into-linq-expressions and http://stackoverflow.com/questions/1618682/linking-a-net-expression新しいアセンブリを作成する – JPW

答えて

12

あなたはパターンマッチングとF#コード名言評価することができます。

open Microsoft.FSharp.Quotations.Patterns 

let rec eval = function 
    | Value(v,t) -> v 
    | Call(None,mi,args) -> mi.Invoke(null, evalAll args) 
    | arg -> raise <| System.NotSupportedException(arg.ToString()) 
and evalAll args = [|for arg in args -> eval arg|] 

let x = eval <@ 1 * 3 @> 

F# PowerPackUnquoteFoq OSSプロジェクトや、より完全な実装については、このsnippetを参照してください。 F#コード見積をコンパイルするに

あなたがdefine a dynamic methodReflection.Emitを使用することができます

open System.Reflection.Emit 

let rec generate (il:ILGenerator) = function 
    | Value(v,t) when t = typeof<int> -> 
     il.Emit(OpCodes.Ldc_I4, v :?> int) 
    | Call(None,mi,args) -> 
     generateAll il args 
     il.EmitCall(OpCodes.Call, mi, null) 
    | arg -> raise <| System.NotSupportedException(arg.ToString()) 
and generateAll il args = for arg in args do generate il arg 

type Marker = interface end 

let compile quotation = 
    let f = DynamicMethod("f", typeof<int>, [||], typeof<Marker>.Module) 
    let il = f.GetILGenerator() 
    quotation |> generate il 
    il.Emit(OpCodes.Ret) 
    fun() -> f.Invoke(null,[||]) :?> int 

let f = compile <@ 1 + 3 @> 
let x = f() 

の方法とタイプを生成するReflection.Emitを使用して、再びアセンブリにコンパイルするには:

open System 
open System.Reflection 

let createAssembly quotation = 
    let name = "GeneratedAssembly" 
    let domain = AppDomain.CurrentDomain 
    let assembly = domain.DefineDynamicAssembly(AssemblyName(name), AssemblyBuilderAccess.RunAndSave) 
    let dm = assembly.DefineDynamicModule(name+".dll") 
    let t = dm.DefineType("Type", TypeAttributes.Public ||| TypeAttributes.Class) 
    let mb = t.DefineMethod("f", MethodAttributes.Public, typeof<int>, [||]) 
    let il = mb.GetILGenerator() 
    quotation |> generate il 
    il.Emit(OpCodes.Ret) 
    assembly.Save("GeneratedAssembly.dll") 

createAssembly <@ 1 + 1 @> 

を参照してください。より完全な実装のためのFilプロジェクト(F#からIL)。

+0

+1新しいプロジェクトを開始するための+1これを行う! – Govert

+0

https://github.com/eiriktsarpalis/QuotationCompilerの実装では、上記のようにReflectionを使用してコード見積もりをコンパイルする必要がなくなりました。この実装では、以前はアクセスできないMicrosoft.FSharp.Compiler名前空間を大きく使用しているようです。 – Sam

+0

@Sam良い点Eirikの引用コンパイラはおそらく今行く方法でしょう –

関連する問題