2011-08-06 10 views
11

Chciałbym wiedzieć, czy istnieje sposób kompilacji cytatu kodu w zespole?F # Jak skompilować ofertę kodu do zestawu

Rozumiem, że jest możliwe, aby zadzwonić CompileUntyped() lub Compile() na obiekcie Exp<_>, na przykład:

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

Jednak, jak mogę utrzymywać com na dysku jako zespół?

Dzięki.

+2

Nie jestem pewien, czy PowerPack obsługuje to. Ale na marginesie, nie zalecałbym używania PowerPacka w ogóle. Ich kod jest często błędny lub zbyt wolny. Napisanie własnego kompilatora lub ewaluatora od podstaw przy użyciu System.Reflection.Emit prawdopodobnie dałoby lepsze wyniki. Istnieje również problem, że F # nie optymalizuje cytatów, na przykład dopasowanie staje się serią if/then/else, zamiast instrukcji skoku jest w CIL w normalnej kompilacji F #. – t0yv0

+0

Cześć, dziękuję za to, domyślam się, że zajrzę do ich alternatyw (takich jak Reflection.Emit jak wspomniałeś). – Ncc

+0

Może się to okazać przydatne: http://stackoverflow.com/questions/2682475/converting-f-quotations-into-linq-expressions i http://stackoverflow.com/questions/1618682/linking-a-net-expression -tree-do-nowego-zestawu – JPW

Odpowiedz

12

Można ocenić F Notowania # kod z dopasowywania wzoru:

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 @> 

Zobacz F# PowerPack, Unquote, Foq projektów OSS czy to snippet Pełniejsze wdrożeń.

skompilować F # kod kosztorysów możesz define a dynamic method użyciu Reflection.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() 

Aby skompilować do zespołu ponownie użyć Reflection.Emit wygenerować typu z metody:

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 @> 

zobaczyć Projekt Fil (F # do IL) dla bardziej kompletnej implementacji.

+0

+1 za rozpoczęcie nowego projektu, który robi to! – Govert

+0

Czy implementacja https://github.com/eiriktsarpalis/QuotationCompiler wyeliminowała potrzebę użycia Reflection jak powyżej, aby skompilować Code Quotation. Wydaje się, że ta implementacja bardzo mocno wykorzystuje przestrzeń nazw Microsoft.FSharp.Compiler, która być może wcześniej nie była dostępna ... – Sam

+0

@Sam dobry punkt Kompilator wyceny Eirika jest prawdopodobnie drogą do zrobienia –