2011-08-06 13 views
11

Vorrei sapere se esiste un modo per compilare una citazione di codice in un assembly?F # Come compilare una citazione di codice per un assembly

Capisco che è possibile chiamare CompileUntyped() o Compile() sull'oggetto Exp<_>, ad esempio:

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

Tuttavia, come posso persistere com disco come un assieme?

Grazie.

+2

Non sono sicuro che PowerPack supporti questo. Ma per inciso, non consiglierei affatto di usare PowerPack. Il loro codice è spesso bacato o troppo lento. Scrivendo il tuo compilatore o valutatore da zero usando System.Reflection.Emit probabilmente darebbe risultati migliori. C'è anche il problema che F # non ottimizza le quotazioni, per esempio la corrispondenza diventa una serie di if/then/else, invece dell'istruzione jump è in CIL nella normale compilazione F #. – t0yv0

+0

Ciao, grazie per questo, credo che cercherò in alternative (come Reflection.Emit come hai menzionato). – Ncc

+0

Potresti trovare utile: http://stackoverflow.com/questions/2682475/converting-f-quotations-into-linq-expressions e http://stackoverflow.com/questions/1618682/linking-a-net-expression -tree-in-a-new-assembly – JPW

risposta

12

È possibile valutare un F # Citazioni codice con pattern matching:

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

Vedi le F# PowerPack, Unquote, Foq progetti OSS o questo snippet per le implementazioni più completi.

Per compilare un F # Codice Quotazione si può define a dynamic method utilizzando 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() 

Per compilare a un assieme di nuovo utilizzare Reflection.Emit per generare un tipo con un metodo:

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

Vedere le Fil progetto (da F # a IL) per un'implementazione più completa.

+0

+1 per l'avvio di un nuovo progetto che fa questo! – Govert

+0

L'implementazione di https://github.com/eiriktsarpalis/QuotationCompiler ha ovviato alla necessità di utilizzare Reflection come sopra per compilare il Codice preventivo. Questa implementazione sembra utilizzare pesantemente lo spazio dei nomi Microsoft.FSharp.Compiler che forse non era accessibile in precedenza ... – Sam

+0

@Sam buon punto Il compilatore di quotazione di Eirik è probabilmente la strada da percorrere adesso –

Problemi correlati