2009-07-21 9 views

Respuesta

19

Aquí hay una pequeña secuencia de comandos que usa el código FSharp CodeDom para compilar una cadena en un ensamblaje y cargarla dinámicamente en la sesión de secuencia de comandos.

Utiliza una extensión del tipo de simplemente permitir defecto muy útiles sobre los argumentos (esperemos que permiten funciones encuadernados apoyarán argumentos opcionales, con nombre y params en un futuro próximo.)

#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 

usar esto en un programa compilado necesitarías la invocación puramente reflexiva.

Por supuesto, este es un juguete en comparación con una API de scripting completo que muchos de nosotros en la comunidad hemos solicitado con urgencia.

mejor de las suertes,

Danny

+0

Eso es impresionante, exactamente lo que necesito! – Cynede

5

¿Está buscando una función Eval?

Es posible que desee intente buscar en esta entrada del blog:

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

Si se lee en sus expresiones en este tipo de estructuras de datos simbólicos, que son bastante fáciles de evaluar.


O, tal vez usted está buscando el apoyo de secuencias de comandos:

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


Si realmente quiere compilación dinámica, que podría hacerlo con el proveedor de F # CodeDom.

3

ha habido un movimiento en este frente. Ahora puede compilar utilizando la muestra sencilla FSharp.Compiler.Service

usando 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 

entonces es una cuestión de Assembly.LoadFrom(dllPath) conseguirlo en el dominio de aplicación actual.

seguido de reflexión-llamadas en base a la DLL (o posiblemente Activator.CreateInstance)

Sample LinqPad Usage

Cuestiones relacionadas