2009-07-21 7 views

Répondre

19

Voici un petit script qui utilise FSharp CodeDom pour compiler une chaîne dans un assembly et la charger dynamiquement dans la session de script.

Il utilise une extension de type simplement pour permettre les valeurs par défaut utiles sur les arguments (LET, espérons-fonctions liées soutiendront en option, les arguments nommés et params dans un avenir proche.)

#r "FSharp.Compiler.dll" 
#r "FSharp.Compiler.CodeDom.dll" 

open System 
open System.IO 
open System.CodeDom.Compiler 
open Microsoft.FSharp.Compiler.CodeDom 

let CompileFSharpString(str, assemblies, output) = 
     use pro = new FSharpCodeProvider() 
     let opt = CompilerParameters(assemblies, output) 
     let res = pro.CompileAssemblyFromSource(opt, [|str|]) 
     if res.Errors.Count = 0 then 
      Some(FileInfo(res.PathToAssembly)) 
     else None 

let (++) v1 v2 = Path.Combine(v1, v2)  
let defaultAsms = [|"System.dll"; "FSharp.Core.dll"; "FSharp.Powerpack.dll"|] 
let randomFile() = __SOURCE_DIRECTORY__ ++ Path.GetRandomFileName() + ".dll" 

type System.CodeDom.Compiler.CodeCompiler with 
    static member CompileFSharpString (str, ?assemblies, ?output) = 
     let assemblies = defaultArg assemblies defaultAsms 
     let output  = defaultArg output (randomFile()) 
     CompileFSharpString(str, assemblies, output)  

// Our set of library functions. 
let library = " 

module Temp.Main 
let f(x,y) = sin x + cos y 
" 
// Create the assembly 
let fileinfo = CodeCompiler.CompileFSharpString(library) 

// Import metadata into the FSharp typechecker 
#r "0lb3lphm.del.dll" 

let a = Temp.Main.f(0.5 * Math.PI, 0.0)  // val a : float = 2.0 

// Purely reflective invocation of the function. 
let asm = Reflection.Assembly.LoadFrom(fileinfo.Value.FullName) 
let mth = asm.GetType("Temp.Main").GetMethod("f") 

// Wrap weakly typed function with strong typing. 
let f(x,y) = mth.Invoke(null, [|box (x:float); box (y:float)|]) :?> float 

let b = f (0.5 * Math.PI, 0.0)    // val b : float = 2.0 

Pour utiliser dans un programme compilé vous auriez besoin de l'invocation purement réflective.

Bien sûr, il s'agit d'un jouet par rapport à une API de script complète que beaucoup d'entre nous dans la communauté ont demandé d'urgence.

bonne chance,

Danny

+0

C'est génial, exactement ce que j'ai besoin! – Cynede

5

Êtes-vous à la recherche d'une fonction Eval?

Vous voudrez peut-être essayer de regarder ce blog:

http://fsharpnews.blogspot.com/2007/02/symbolic-manipulation.html

Si vous lisez dans vos expressions dans ce genre de datastructures symboliques, ils sont assez faciles à évaluer.


Ou, peut-être vous êtes à la recherche d'un soutien de script:

http://blogs.msdn.com/chrsmith/archive/2008/09/12/scripting-in-f.aspx


Si vous voulez vraiment la compilation dynamique, vous pourriez le faire avec le fournisseur F # CodeDom.

3

Il a été sur ce front. Vous pouvez maintenant compiler en utilisant la FSharp.Compiler.Service

simple échantillon à l'aide FSharp.Compiler.Service 5.0.0 de NuGet

open Microsoft.FSharp.Compiler.SimpleSourceCodeServices 
let compile (codeText:string) = 
    let scs = SimpleSourceCodeServices() 
    let src,dllPath = 
     let fn = Path.GetTempFileName() 
     let fn2 = Path.ChangeExtension(fn, ".fs") 
     let fn3 = Path.ChangeExtension(fn, ".dll") 
     fn2,fn3 
    File.WriteAllText(src,codeText) 
    let errors, exitCode = scs.Compile [| "fsc.exe"; "-o"; dllPath; "-a";src; "-r"; "WindowsBase"; "-r" ;"PresentationCore"; "-r"; "PresentationFramework" |] 
    match errors,exitCode with 
    | [| |],0 -> Some dllPath 
    | _ -> 
     (errors,exitCode).Dump("Compilation failed") 
     File.Delete src 
     File.Delete dllPath 
     None 

il est une question de Assembly.LoadFrom(dllPath) pour l'obtenir dans le domaine de l'application actuelle.

suivie par une réflexion basée sur les appels dans le dll (ou peut-être Activator.CreateInstance)

Sample LinqPad Usage