How to execute F# code found in a string in a compiled F# program?

后端 未结 3 731
不思量自难忘°
不思量自难忘° 2020-12-14 10:39

How do I execute F# code from a string in a compiled F# program?

相关标签:
3条回答
  • 2020-12-14 11:21

    Are you looking for an Eval function?

    You might want to try looking at this blog post:

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

    If you read in your expressions into these kind of symbolic datastructures, they are pretty easy to evaluate.


    Or, perhaps you are looking for scripting support:

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


    If you really want dynamic compilation, you could do it with the F# CodeDom provider.

    0 讨论(0)
  • 2020-12-14 11:27

    There has been movement on this front. You can now compile using the FSharp.Compiler.Service

    simple sample using FSharp.Compiler.Service 5.0.0 from 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
    

    then it's a matter of Assembly.LoadFrom(dllPath) to get it into the current app domain.

    followed by reflection based-calls into the dll (or possibly Activator.CreateInstance)

    Sample LinqPad Usage

    0 讨论(0)
  • 2020-12-14 11:37

    Here's a little script that uses the FSharp CodeDom to compile a string into an assembly and dynamically load it into the script session.

    It uses a type extension simply to allow useful defaults on the arguments (hopefully let bound functions will support optional, named and params arguments in the near future.)

    #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
    

    To use this in a compiled program you would need the purely reflective invocation.

    Of course this is a toy compared to a full scripting API that many of us in the community have urgently requested.

    best of luck,

    Danny

    0 讨论(0)
提交回复
热议问题