如何在[<reflectdefinition>]标记模块中获取函数的AST?

时间:2016-02-23 11:55:38

标签: f#

[<ReflectedDefinition>]
module Foo = 
    let x = 5
    let y () = 6
    let z a = a

我试图找出如何在这种情况下多次获得AST,并且一直都在失败。是时候在这里提问了。

到目前为止,我认为一个模块将被内部映射到具有静态成员的类,因此,它应该相当于:

[<ReflectedDefinition>] 
type Foo =
    static member x = 5
    static member y () = 6
    static member z a = a

let bar_members = 
    typeof<Bar>.GetMethods()
    |> Array.filter (fun mi -> match mi with | MethodWithReflectedDefinition x -> true | _ -> false)
    |> Array.map (fun m -> sprintf "%s: %A" (m.Name) (Expr.TryGetReflectedDefinition(m :> MethodBase) ) )

在后一种情况下,我可以使用typeof<Foo>.GetMembers()(或GetMethods() ?!),将其投放到Reflection.MethodBase并将其用作Expr.TryGetReflectedDefinition()的参数。

但不幸的是,这不适用于模块版本。

那么,怎么做呢?

如果您想使用代码,可能需要打开一些名称空间:

open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.DerivedPatterns
open Microsoft.FSharp.Reflection
open System.Reflection

2 个答案:

答案 0 :(得分:3)

问题来自于实际获取模块的类型。为了做到这一点,Phillip Trelford在这里给出了一个很好的答案:https://stackoverflow.com/a/14706890/5438433

基本上,您向模块添加一个辅助值,该值返回该模块的类型:

[<ReflectedDefinition>]
module Foo = 
    type internal IMarker = interface end
    let fooType = typeof<IMarker>.DeclaringType

    let x = 5
    let y () = 6
    let z a = a

然后,您可以使用fooType来检索反映的定义。

 let foo_members = 
     Foo.fooType.GetMethods()
     |> Array.filter (fun mi -> match mi with | MethodWithReflectedDefinition x -> true | _ -> false)
     |> Array.map (fun m -> sprintf "%s: %A" (m.Name) (Expr.TryGetReflectedDefinition(m :> MethodBase) ) )

我可以,例如打印结果:

  

[|&#34; get_fooType:一些PropertyGet(Some(Call(None,TypeOf,[])),DeclaringType,[])&#34 ;;   &#34; get_x:一些值(5)&#34 ;;   &#34; y:一些Lambda(unitVar0,Value(6))&#34 ;;   &#34; z:一些Lambda(a,a)&#34; |]

答案 1 :(得分:0)

对于用例,当反射的定义在另一个程序集中时(例如,像F#dll),您可以不使用标记接口技巧,如下所示:

open System
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.DerivedPatterns
open Microsoft.FSharp.Reflection
open System.Reflection
open FSharp.Reflection.FSharpReflectionExtensions

let tryGetReflectedModules (a : Assembly) : seq<TypeInfo>  = 
    a.DefinedTypes 
    |> Seq.filter 
        (fun dt -> 
            dt.CustomAttributes
            |> Seq.map (fun cad -> cad.AttributeType)
            |> Seq.filter ((=) (typeof<ReflectedDefinitionAttribute>))
            |> Seq.isEmpty
            |> not
        )

let astFromReflectedDefinition (mi : MethodInfo) : Expr option =
    mi :> MethodBase |> Expr.TryGetReflectedDefinition

let reflectedMethodsOfAModule (m : System.Type) : (MethodInfo * Expr) [] =
    m.GetMethods()
    |> Array.map (fun m -> (m,astFromReflectedDefinition m))
    |> Array.filter (snd >> Option.isSome)
    |> Array.map (fun (x,y) -> (x, Option.get y))

let reflectAssembly (assemblyPath : string) =
    let a = System.Reflection.Assembly.LoadFile(assemblyPath) 
    a 
    |> tryGetReflectedModules
    |> Seq.map (fun x -> (x,reflectedMethodsOfAModule (x.AsType())))

例如,我用于测试上面代码的程序集如下所示:

namespace Input

[<ReflectedDefinition>]
module Api =
    let trace s = 
        for _ in [0..3] do System.Diagnostics.Trace.WriteLine s

[<ReflectedDefinition>]
module Foo =
    let foobar (x : string) : string =
        x.ToUpper()

您可以获得程序集中的顶级类型,这恰好是(静态)类,表示Fsharp程序集的模块并测试ReflectedDefinitionAttribute存在。然后,你从那里拿走它。