forked from fslaborg/RProvider
-
Notifications
You must be signed in to change notification settings - Fork 0
/
RProvider.fs
100 lines (83 loc) · 5.62 KB
/
RProvider.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
namespace RProvider
open System
open System.Collections.Generic
open System.Reflection
open System.IO
open System.Diagnostics
open System.Threading
open Samples.FSharp.ProvidedTypes
open Microsoft.FSharp.Core.CompilerServices
open RDotNet
open RInteropInternal
open RInterop
[<TypeProvider>]
type public RProvider(cfg:TypeProviderConfig) as this =
inherit TypeProviderForNamespaces()
// Get the assembly and namespace used to house the provided types
let asm = System.Reflection.Assembly.GetExecutingAssembly()
let ns = "RProvider"
// Expose all available packages as namespaces
do
for package in getPackages() do
let pns = ns + "." + package
let pty = ProvidedTypeDefinition(asm, pns, "R", Some(typeof<obj>))
pty.AddXmlDocDelayed <| fun () -> getPackageDescription package
pty.AddMembersDelayed( fun () ->
[ loadPackage package
let bindings = getBindings package
// We get the function descriptions for R the first time they are needed
let titles = lazy getFunctionDescriptions package
for name, rval in Map.toSeq bindings do
let memberName = makeSafeName name
match rval with
| RValue.Function(paramList, hasVarArgs) ->
let paramList = [ for p in paramList ->
ProvidedParameter(makeSafeName p, typeof<obj>, optionalValue=null)
if hasVarArgs then
yield ProvidedParameter("paramArray", typeof<obj[]>, optionalValue=null, isParamArray=true)
]
let paramCount = paramList.Length
let pm = ProvidedMethod(
methodName = memberName,
parameters = paramList,
returnType = typeof<SymbolicExpression>,
IsStaticMethod = true,
InvokeCode = fun args -> if args.Length <> paramCount then
failwithf "Expected %d arguments and received %d" paramCount args.Length
if hasVarArgs then
let namedArgs =
Array.sub (Array.ofList args) 0 (paramCount-1)
|> List.ofArray
let namedArgs = Quotations.Expr.NewArray(typeof<obj>, namedArgs)
let varArgs = args.[paramCount-1]
<@@ RInterop.call package name %%namedArgs %%varArgs @@>
else
let namedArgs = Quotations.Expr.NewArray(typeof<obj>, args)
<@@ RInterop.call package name %%namedArgs [||] @@> )
pm.AddXmlDocDelayed (fun () -> match titles.Value.TryFind name with
| Some docs -> docs
| None -> "No documentation available")
yield pm :> MemberInfo
// Yield an additional overload that takes a Dictionary<string, object>
// This variant is more flexible for constructing lists, data frames etc.
let pdm = ProvidedMethod(
methodName = memberName,
parameters = [ ProvidedParameter("paramsByName", typeof<IDictionary<string,obj>>) ],
returnType = typeof<SymbolicExpression>,
IsStaticMethod = true,
InvokeCode = fun args -> if args.Length <> 1 then
failwithf "Expected 1 argument and received %d" args.Length
let argsByName = args.[0]
<@@ let vals = %%argsByName: IDictionary<string,obj>
let valSeq = vals :> seq<KeyValuePair<string, obj>>
RInterop.callFunc package name valSeq null @@> )
yield pdm :> MemberInfo
| RValue.Value ->
yield ProvidedProperty(
propertyName = memberName,
propertyType = typeof<SymbolicExpression>,
IsStatic = true,
GetterCode = fun _ -> <@@ RInterop.call package name [| |] [| |] @@>) :> MemberInfo ] )
this.AddNamespace(pns, [ pty ])
[<TypeProviderAssembly>]
do()