2011-08-06 23 views
11

Tôi muốn biết nếu có cách nào để biên dịch một mã báo giá thành một assembly?F # Làm thế nào để biên dịch một mã báo giá thành một assembly

Tôi hiểu rằng người ta có thể gọi CompileUntyped() hoặc Compile() trên đối tượng Exp<_>, ví dụ:

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

Tuy nhiên, làm thế nào tôi có thể tồn tại com vào đĩa như một hội đồng?

Cảm ơn.

+2

Tôi không chắc liệu PowerPack có hỗ trợ điều này hay không. Nhưng như một sang một bên, tôi sẽ không khuyên bạn nên sử dụng PowerPack. Mã của họ thường bị lỗi hoặc quá chậm. Viết trình biên dịch hoặc bộ đánh giá của riêng bạn từ đầu bằng System.Reflection.Emit có thể sẽ cho kết quả tốt hơn. Ngoài ra còn có vấn đề là F # không tối ưu hóa các trích dẫn, ví dụ kết hợp sẽ trở thành một chuỗi nếu/sau đó/khác, thay vì lệnh nhảy, nó nằm trong CIL trong quá trình biên dịch F # bình thường. – t0yv0

+0

Xin chào, cảm ơn vì điều này, tôi đoán tôi sẽ xem xét các lựa chọn thay thế cho họ (chẳng hạn như Reflection.Emit như bạn đã đề cập). – Ncc

+0

Bạn có thể thấy điều này hữu ích: http://stackoverflow.com/questions/2682475/converting-f-quotations-into-linq-expressions và http://stackoverflow.com/questions/1618682/linking-a-net-expression -tree-in-a-new-assembly – JPW

Trả lời

12

Bạn có thể đánh giá một chiếc F Báo giá # Mã với mô hình kết hợp:

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

Xem F# PowerPack, Unquote, Foq dự án OSS hoặc snippet này cho việc triển khai hoàn chỉnh hơn.

Để biên dịch một F # Mã báo giá bạn có thể sử dụng 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() 

Để biên dịch cho một assembly một lần nữa sử dụng Reflection.Emit để tạo ra một kiểu với một phương pháp:

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

Xem Fil dự án (F # đến IL) để thực hiện đầy đủ hơn.

+0

+1 để bắt đầu một dự án mới thực hiện điều này! – Govert

+0

Việc triển khai https://github.com/eiriktsarpalis/QuotationCompiler đã làm giảm sự cần thiết phải sử dụng Phản ánh như trên để biên dịch Báo giá mã. Triển khai này dường như sử dụng không gian tên Microsoft.FSharp.Compiler rất nhiều mà có lẽ trước đây không thể truy cập được ... – Sam

+0

@Sam điểm tốt Trình biên dịch báo giá của Eirik có lẽ là cách để đi ngay bây giờ –

Các vấn đề liên quan