F # Как скомпилировать цитату в сборку - PullRequest
12 голосов
/ 06 августа 2011

Хотелось бы узнать, есть ли способ скомпилировать цитату в сборку?

Я понимаю, что можно вызвать CompileUntyped() или Compile() для объекта Exp<_>,например:

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

Однако как мне сохранить com на диске как сборку?

Спасибо.

1 Ответ

12 голосов
/ 09 апреля 2013

Вы можете оценить цитаты кода 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 # PowerPack , Unquote , Foq OSS или этот фрагмент для более полной реализации.

Для составления цитаты кода F # вы можете определить динамический метод , используя 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 ()

Чтобы снова скомпилировать сборку, используйте 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) для более полной реализации.

...