2011-08-06 24 views
11

Me gustaría saber si hay una manera de compilar una cita de código en un ensamblado?F # Cómo compilar una cita de código para un ensamblaje

entiendo que es posible llamar CompileUntyped() o Compile() en el objeto Exp<_>, por ejemplo:

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

Sin embargo, la forma en que pueden persistir com en el disco como un montaje?

Gracias.

+2

No estoy seguro de si PowerPack es compatible con esto. Pero aparte, no recomendaría usar PowerPack en absoluto. Su código es a menudo con errores o muy lento. Escribir su propio compilador o evaluador desde cero utilizando System.Reflection.Emit probablemente dará mejores resultados. También existe el problema de que F # no optimiza las citas, por ejemplo, la coincidencia se convierte en una serie de if/then/else, en lugar de la instrucción de salto en CIL en la compilación normal de F #. – t0yv0

+0

Hola, gracias por esto, creo que buscaré alternativas (como Reflection.Emit como mencionaste). – Ncc

+0

Puede encontrar esto útil: http://stackoverflow.com/questions/2682475/converting-f-quotations-into-linq-expressions y http://stackoverflow.com/questions/1618682/linking-a-net-expression -tree-into-a-new-assembly – JPW

Respuesta

12

Usted puede evaluar un F # Código Citas con coincidencia de patrones:

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

Ver las F# PowerPack, Unquote, Foq proyectos de software libre o esta snippet para las implementaciones más completas.

Compilar un Código F # Cotización puedas define a dynamic method usando 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() 

Compilar a un conjunto nuevo Reflection.Emit utilizar para generar un tipo con un método:

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

Véase el Fil proyecto (F # a IL) para una implementación más completa.

+0

+1 para iniciar un nuevo proyecto que ¡Haz esto! – Govert

+0

Tiene la implementación de https://github.com/eiriktsarpalis/QuotationCompiler obvió la necesidad de utilizar Reflection como se indica anteriormente para compilar el Código de oferta. Esta implementación parece utilizar el espacio de nombres de Microsoft.FSharp.Compiler en gran medida, que tal vez no era accesible anteriormente ... – Sam

+0

@Sam buen punto El compilador de citas de Eirik es probablemente el camino a seguir ahora –

Cuestiones relacionadas